此外,这种模型的应用(如租赁)通常要求用户地址只能临时访问使用 NFT。通常,这意味着所有者需要提交两个链上交易,一个交易在持续时间开始时将新地址列为新的用户角色,另一个交易在结束时回收用户角色。这在劳动力和 gas 方面都是低效的,因此引入了一个“expires”函数,该函数将有助于自动结束使用期限,而无需第二次交易。
interfaceIERC4907{// Logged when the user of an NFT is changed or expires is changed
/// @notice 当 NFT 的 `user` 更改或 `user` 的 `expires` 更改时发出
/// 当 user 的地址为零地址时,表示没有 user 地址
eventUpdateUser(uint256indexedtokenId,addressindexeduser,uint64expires);/// @notice 设置 NFT 的 user 和 expires
/// @dev 零地址表示没有 user
/// 如果 `tokenId` 不是有效的 NFT 则抛出
/// @param user NFT 的新 user
/// @param expires UNIX 时间戳,新的 user 可以在 expires 之前使用 NFT
functionsetUser(uint256tokenId,addressuser,uint64expires)external;/// @notice 获取 NFT 的 user 地址
/// @dev 零地址表示没有 user 或 user 已过期
/// @param tokenId 要获取 user 地址的 NFT
/// @return 此 NFT 的 user 地址
functionuserOf(uint256tokenId)externalviewreturns(address);/// @notice 获取 NFT 的 user 过期时间
/// @dev 零值表示没有 user
/// @param tokenId 要获取 user 过期时间的 NFT
/// @return 此 NFT 的 user 过期时间
functionuserExpires(uint256tokenId)externalviewreturns(uint256);}
userOf(uint256 tokenId) 函数可以实现为 pure 或 view。
userExpires(uint256 tokenId) 函数可以实现为 pure 或 view。
setUser(uint256 tokenId, address user, uint64 expires) 函数可以实现为 public 或 external。
// SPDX-License-Identifier: CC0-1.0
pragmasolidity^0.8.0;import"@openzeppelin/contracts/token/ERC721/ERC721.sol";import"./IERC4907.sol";contractERC4907isERC721,IERC4907{structUserInfo{addressuser;// address of user role
uint64expires;// unix timestamp, user expires
}mapping(uint256=>UserInfo)internal_users;constructor(stringmemoryname_,stringmemorysymbol_)ERC721(name_,symbol_){}/// @notice set the user and expires of an NFT
/// @dev The zero address indicates there is no user
/// Throws if `tokenId` is not valid NFT
/// @param user The new user of the NFT
/// @param expires UNIX timestamp, The new user could use the NFT before expires
functionsetUser(uint256tokenId,addressuser,uint64expires)publicvirtual{require(_isApprovedOrOwner(msg.sender,tokenId),"ERC4907: transfer caller is not owner nor approved");UserInfostorageinfo=_users[tokenId];info.user=user;info.expires=expires;emitUpdateUser(tokenId,user,expires);}/// @notice Get the user address of an NFT
/// @dev The zero address indicates that there is no user or the user is expired
/// @param tokenId The NFT to get the user address for
/// @return The user address for this NFT
functionuserOf(uint256tokenId)publicviewvirtualreturns(address){if(uint256(_users[tokenId].expires)>=block.timestamp){return_users[tokenId].user;}else{returnaddress(0);}}/// @notice Get the user expires of an NFT
/// @dev The zero value indicates that there is no user
/// @param tokenId The NFT to get the user expires for
/// @return The user expires for this NFT
functionuserExpires(uint256tokenId)publicviewvirtualreturns(uint256){return_users[tokenId].expires;}/// @dev See {IERC165-supportsInterface}.
functionsupportsInterface(bytes4interfaceId)publicviewvirtualoverridereturns(bool){returninterfaceId==type(IERC4907).interfaceId||super.supportsInterface(interfaceId);}function_beforeTokenTransfer(addressfrom,addressto,uint256tokenId)internalvirtualoverride{super._beforeTokenTransfer(from,to,tokenId);if(from!=to&&_users[tokenId].user!=address(0)){delete_users[tokenId];emitUpdateUser(tokenId,address(0),0);}}}