ERC-4907 is a standard that has been proposed by renting protocols to allow NFTs to be rented and accepted by the ecosystem. This standard makes it possible to create non-fungible digital assets that allow you the possibility of using the rights of the NFT instead of having ownership over it. This new standards extendes the ERC721 standard and introduces a "User" role in addition to the "Owner" role that exists initially.
interface IERC4907 {// Logged when the user of a token assigns a new user or updates expires/// @notice Emitted when the `user` of an NFT or the `expires` of the `user` is changed/// The zero address for user indicates that there is no user addresseventUpdateUser(uint256indexed tokenId, addressindexed user, uint64 expires);/// @notice set the user and expires of a 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 expiresfunctionsetUser(uint256 tokenId,address user,uint64 expires) external ;/// @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 NFTfunctionuserOf(uint256 tokenId) externalviewreturns(address);/// @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 NFTfunctionuserExpires(uint256 tokenId) externalviewreturns(uint256);}
Reference Implementation
/// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4907.md/// SPDX-License-Identifier: CC0-1.0pragmasolidity ^0.8.0;import"@openzeppelin/contracts/token/ERC721/ERC721.sol";import"./IERC4907.sol";/// You can choose the deferenct implementation of ERC721/// case 1: contract ERC4907 is ERC721, IERC4907/// case 2: contract ERC4907 is ERC721Enumerable, IERC4907/// case 3: contract ERC4907 is ERC721Burnable, IERC4907contractERC4907isERC721, IERC4907 {structUserInfo {address user; // address of user roleuint64 expires; // unix timestamp, user expires }mapping (uint256=> UserInfo) internal _users;constructor(stringmemory name_,stringmemory symbol_)ERC721(name_, symbol_) { }/// @dev See {IERC4907-setUser}functionsetUser(uint256 tokenId,address user,uint64 expires) publicvirtual {require(_isApprovedOrOwner(msg.sender, tokenId),"ERC4907: transfer caller is not owner nor approved"); UserInfo storage info = _users[tokenId]; info.user = user; info.expires = expires;emitUpdateUser(tokenId, user, expires); }/// @dev See {IERC4907-userOf}functionuserOf(uint256 tokenId) publicviewvirtualreturns(address){if( uint256(_users[tokenId].expires) >= block.timestamp){return _users[tokenId].user; }else{returnaddress(0); } }/// @dev See {IERC4907-userExpires}functionuserExpires(uint256 tokenId) publicviewvirtualreturns(uint256){return _users[tokenId].expires; }/// @dev See {IERC165-supportsInterface}functionsupportsInterface(bytes4 interfaceId) publicviewvirtualoverridereturns (bool) {return interfaceId == type(IERC4907).interfaceId || super.supportsInterface(interfaceId); }/// @dev delete UserInfo when burnfunction_burn(uint256 tokenId) internalvirtualoverride {delete _users[tokenId];emitUpdateUser(tokenId,address(0),0); super._burn(tokenId); }}