符合 EIP-721 的合约 MAY 实现此 EIP,以提供在其当前所有者地址锁定和解锁 token 的标准方法。
如果 token 被锁定,则 getLocked 函数 MUST 返回一个能够解锁 token 的地址。
对于未锁定的 token,getLocked 函数 MUST 返回 address(0)。
用户 MAY 通过调用 lock(address(1), tokenId) 永久锁定该 token。
当 token 被锁定时,所有 EIP-721 转移函数 MUST 恢复,除非该交易是由解锁器发起的。
当 token 被锁定时,EIP-721approve 方法 MUST 恢复此 token。
当 token 被锁定时,EIP-721getApproved 方法 SHOULD 为此 token 返回 unlocker 地址,以便解锁器能够转移此 token。
当锁定的 token 由解锁器转移时,token MUST 在转移后解锁。
市场应调用 EIP-721 Lockable token 合约的 getLocked 方法,以了解具有指定 tokenId 的 token 是否被锁定。锁定的 token SHOULD NOT 可用于列表。锁定的 token 无法出售。因此,市场 SHOULD 隐藏已锁定 token 的列表,因为此类订单无法完成。
合约接口
pragmasolidity>=0.8.0;/// @dev Interface for the Lockable extension
/// @dev Lockable 扩展的接口
interfaceILockable{/**
* @dev Emitted when `id` token is locked, and `unlocker` is stated as unlocking wallet.
* @dev 当 `id` token 被锁定并且 `unlocker` 被声明为解锁钱包时发出。
*/eventLock(addressindexedunlocker,uint256indexedid);/**
* @dev Emitted when `id` token is unlocked.
* @dev 当 `id` token 被解锁时发出。
*/eventUnlock(uint256indexedid);/**
* @dev Locks the `id` token and gives the `unlocker` address permission to unlock.
* @dev 锁定 `id` token 并授予 `unlocker` 地址解锁权限。
*/functionlock(addressunlocker,uint256id)external;/**
* @dev Unlocks the `id` token.
* @dev 解锁 `id` token。
*/functionunlock(uint256id)external;/**
* @dev Returns the wallet, that is stated as unlocking wallet for the `tokenId` token.
* If address(0) returned, that means token is not locked. Any other result means token is locked.
* @dev 返回声明为 `tokenId` token 的解锁钱包的钱包。
* 如果返回 address(0),则表示 token 未锁定。任何其他结果都表示 token 已锁定。
*/functiongetLocked(uint256tokenId)externalviewreturns(address);}
当使用 0x72b68110 调用 supportsInterface 方法时,它 MUST 返回 true。
// SPDX-License-Identifier: CC0-1.0
pragmasolidity>=0.8.0;import'../ILockable.sol';import'@openzeppelin/contracts/token/ERC721/ERC721.sol';/// @title Lockable Extension for ERC721
/// @title ERC721 的可锁定扩展
abstractcontractERC721LockableisERC721,ILockable{/*///////////////////////////////////////////////////////////////
LOCKABLE EXTENSION STORAGE
//////////////////////////////////////////////////////////////*/mapping(uint256=>address)internalunlockers;/*///////////////////////////////////////////////////////////////
LOCKABLE LOGIC
//////////////////////////////////////////////////////////////*//**
* @dev Public function to lock the token. Verifies if the msg.sender is the owner
* or approved party.
* @dev 用于锁定 token 的公共函数。验证 msg.sender 是否为所有者
* 或批准方。
*/functionlock(addressunlocker,uint256id)publicvirtual{addresstokenOwner=ownerOf(id);require(msg.sender==tokenOwner||isApprovedForAll(tokenOwner,msg.sender),"NOT_AUTHORIZED");require(unlockers[id]==address(0),"ALREADY_LOCKED");unlockers[id]=unlocker;_approve(unlocker,id);}/**
* @dev Public function to unlock the token. Only the unlocker (stated at the time of locking) can unlock
* @dev 用于解锁 token 的公共函数。只有解锁器(在锁定时声明)可以解锁
*/functionunlock(uint256id)publicvirtual{require(msg.sender==unlockers[id],"NOT_UNLOCKER");unlockers[id]=address(0);}/**
* @dev Returns the unlocker for the tokenId
* address(0) means token is not locked
* reverts if token does not exist
* @dev 返回 tokenId 的解锁器
* address(0) 表示 token 未锁定
* 如果 token 不存在则恢复
*/functiongetLocked(uint256tokenId)publicvirtualviewreturns(address){require(_exists(tokenId),"Lockable: locking query for nonexistent token");returnunlockers[tokenId];}/**
* @dev Locks the token
* @dev 锁定 token
*/function_lock(addressunlocker,uint256id)internalvirtual{unlockers[id]=unlocker;}/**
* @dev Unlocks the token
* @dev 解锁 token
*/function_unlock(uint256id)internalvirtual{unlockers[id]=address(0);}/*///////////////////////////////////////////////////////////////
OVERRIDES
//////////////////////////////////////////////////////////////*/functionapprove(addressto,uint256tokenId)publicvirtualoverride{require(getLocked(tokenId)==address(0),"Can not approve locked token");super.approve(to,tokenId);}function_beforeTokenTransfer(addressfrom,addressto,uint256tokenId)internalvirtualoverride{// if it is a Transfer or Burn
// 如果是转移或销毁
if(from!=address(0)){// token should not be locked or msg.sender should be unlocker to do that
// token 不应被锁定,或者 msg.sender 应该是解锁器才能执行此操作
require(getLocked(tokenId)==address(0)||msg.sender==getLocked(tokenId),"LOCKED");}}function_afterTokenTransfer(addressfrom,addressto,uint256tokenId)internalvirtualoverride{// if it is a Transfer or Burn, we always deal with one token, that is startTokenId
// 如果是转移或销毁,我们始终处理一个 token,即 startTokenId
if(from!=address(0)){// clear locks
// 清除锁
deleteunlockers[tokenId];}}/**
* @dev Optional override, if to clear approvals while the tken is locked
* @dev 可选的覆盖,如果在锁定 tken 时清除批准
*/functiongetApproved(uint256tokenId)publicviewvirtualoverridereturns(address){if(getLocked(tokenId)!=address(0)){returnaddress(0);}returnsuper.getApproved(tokenId);}/*///////////////////////////////////////////////////////////////
ERC165 LOGIC
//////////////////////////////////////////////////////////////*/functionsupportsInterface(bytes4interfaceId)publicviewvirtualoverridereturns(bool){returninterfaceId==type(IERC721Lockable).interfaceId||super.supportsInterface(interfaceId);}}