I will be happy to chat more. Please check this out: → EIP-tbd Rental & Delegation NFT - ERC-721 Extension
Great, i would like to chat more. Please, check this out: → EIP-tbd Rental & Delegation NFT - ERC-721 Extension
Great point, please check this out: → EIP-tbd Rental & Delegation NFT - ERC-721 Extension And let us know, what you think.
What is the consensus on this EIP? What projects are using it?
This seems like a pretty serious issue that I have not seen addressed:
When I was testing ERC-4907 contract, I found out something. Let’s say an NFT with token id:1 was minted on user(1) and rented to user(2) for 3 days. Now during these 3 days, whenever user(1) transfers that NFT to let’s assume user(3), user(2) which should have access to rented NFT for 3 days, now no longer have access to that NFT. userOf(1) sets to address(0). This can be overcome by adding modifiers and overriding transfer and safeTransfer functions of ERC-721.
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "./IERC4907.sol";
contract ERC4907 is ERC721, IERC4907 {
using Counters for Counters.Counter;
Counters.Counter private _tokenIdCounter;
struct UserInfo
{
address user; // address of user role
uint64 expires; // unix timestamp, user expires
}
mapping (uint256 => UserInfo) internal _users;
modifier notRented(uint256 tokenId){
require(userOf(tokenId)==address(0),"can not transfer rented NFT");
_;
}
constructor(string memory name_, string memory symbol_)
ERC721(name_,symbol_)
{
}
function safeMint(address to) public {
_tokenIdCounter.increment();
uint256 tokenId = _tokenIdCounter.current();
_safeMint(to, tokenId);
}
/// @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 expires
function setUser(uint256 tokenId, address user, uint64 expires) public virtual{
require(_isApprovedOrOwner(msg.sender, tokenId),"ERC721: transfer caller is not owner nor approved");
UserInfo storage info = _users[tokenId];
info.user = user;
info.expires = expires;
emit UpdateUser(tokenId,user,expires);
}
function getTimestamp()public view returns(uint256){
return block.timestamp +300;
}
/// @notice Get the user1664725514 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
function userOf(uint256 tokenId)public view virtual returns(address){
if( uint256(_users[tokenId].expires) >= block.timestamp){
return _users[tokenId].user;
}
else{
return address(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
function userExpires(uint256 tokenId) public view virtual returns(uint256){
return _users[tokenId].expires;
}
/// @dev See {IERC165-supportsInterface}.
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC4907).interfaceId || super.supportsInterface(interfaceId);
}
function _beforeTokenTransfer(
address from,
address to,
uint256 tokenId
) internal virtual override{
super._beforeTokenTransfer(from, to, tokenId);
if (from != to && _users[tokenId].user != address(0)) {
delete _users[tokenId];
emit UpdateUser(tokenId, address(0), 0);
}
}
function transferFrom(
address from,
address to,
uint256 tokenId
) public virtual override notRented(tokenId){
//solhint-disable-next-line max-line-length
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
_transfer(from, to, tokenId);
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) public virtual override notRented(tokenId){
safeTransferFrom(from, to, tokenId, "");
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes memory data
) public virtual override notRented(tokenId){
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: caller is not token owner or approved");
_safeTransfer(from, to, tokenId, data);
}
}
Hey mate, great documentation. Are these features approved and ready to be implemented into any dApp ? or do you need to get it approved first.
thank you
For rental protocol, to protect the use rights, the ERC-4907 NFT can be staked to a contract.
Double Protocol Lending Example:
Glossary:
- oNFT: original NFT, which is an ERC-4907 NFT
- doNFT: DOUBLE NFT. doNFT complies with the ERC-721 standard and represents a certificate for the right to use the NFT within a specific time.
- vNFT: A special doNFT, as a voucher to redeem the original NFT.
Steps:
1 Alice calls setApprovalForAll() function of oNFT contract, approves doNFT contract can transfer the oNFT token.
2 Alice calls mintVNft() function of the doNFT contract to mint a vNFT token:
2.1 doNFT contract calls transferFrom() function of oNFT contract.
2.2 doNFT contract transfers the oNFT token to the doNFT contract. After that, the owner of the oNFT will become the doNFT contract.
2.3 doNFT contract mints a vNFT token and transfers it to Alice.
2.4 doNFT contract calls setUser() function of oNFT contract.
2.5 Set user to Alice so that Alice can use the oNFT when the oNFT is not rented.
3 Alice calls createLendOrder() function of the market contract, sets the rental price, period, and creates a lending order in the market.
More details: ERC-4907 Model Timing Diagram - Double
Some NFTs can be rented on double.one:
Some ERC-4907 contracts:
symbol | chain | contract address |
---|---|---|
POP | Ethereum | 0xe1fb64bcefd7392aa0ec39354618ac48a5ddc501 |
INF | Ethereum | 0x8e5324d34ee9ab2ed84ac9ba237ca0433e89130c |
OSF | Ethereum | 0x715cbae9d7fcf899e69221368be587f6cb39e4c6 |
GAVA | Ethereum | 0x4302b24ea1010b4cb4f1f056341334ccd2efd3c1 |
AFP | Ethereum | 0xa13b8cbd068fe176b3d37c6694173064e17ef563 |
MDT | Ethereum | 0x86eea52fd8ed3ee84bc135b284a589e4ed4ebf14 |
ADCCC | Ethereum | 0x4711e4ea574c72501be4922224cfa38bc281d16d |
eyecon | Ethereum | 0x8d95a85f5f6dafeffad29dfe08869c1f9dd00802 |
METAHOME | Ethereum | 0x3872beeb0edef42f39e3b8e7d5339352f22d319a |
XSOLDIERS | Ethereum | 0x0e559f7771d8fbd2dda30a4eae12c07179907d53 |
NTR | Ethereum | 0xbf8f831c9b90038fdfbf034740f624fdc63f3217 |
MD | Ethereum | 0xbedee3879a3c5107d8846bf7d91dbaacf8d42544 |
SHARKZG | Ethereum | 0x91220b88311dcd521e0d68466ef39514a1baeebc |
cocoisland | Ethereum | 0xd36c2731078d8490bae33ba5e05bf8b99abdc913 |
VXIN | Ethereum | 0x2f31f763911a79fccefd3014b1fdb9dd8dbadf7f |
wetw | Ethereum | 0xd446e68714b61acdb2c51076fe16f127b94c9328 |
LSC | Ethereum | 0x491f8d040e90a8a9681f6afe9dfb114f2dbb960c |
MCAstro | Ethereum | 0x9df90628d40c72f85137e8cee09dde353a651266 |
SUSUALPHA | Ethereum | 0x29e0a58f62a34a29965ac5c64f5f3c792bee7a9a |
HMLAP | Ethereum | 0x0d618537548d769bd30f5c8e19f2b8beb7d83f06 |
CAHMAP | Ethereum | 0x512926bc2266db6546f3d3c11a383b3d0bf19a11 |
ZAHMAP | Ethereum | 0x44da50f9ce1557cb8b9e04bda0fb9e84172a44b7 |
T2WEB | Ethereum | 0x75d0bf98e2dbf8248c2545e4fbbb1c0247ef7508 |
wWarena | BSC | 0x6793619c3dd9d545520923da47c6f8824e2cc30f |
SKNFT | BSC | 0x9835b7731017f52adadbb1d9f2e3aaa88c7d5db6 |
EGNFT | BSC | 0xe5fea8173f817a758f265c5d8f23790df8a311e0 |
MECH_HERO | BSC | 0x959924fcfb62cbd1b2e4ed50fadd1b469f0496b8 |
BSONE | BSC | 0x01fccf2b10c0b3d579e98574ef0488f1b05a3824 |
BBBONE | BSC | 0xadf74b09c6fead7cbac1c21eb3a583f4b2e10cf1 |
EGNFT | BSC | 0x22eb0f44406e7d0631600c5efa3158f648825e95 |
T2WEB | BSC | 0x94a3695e8a2e7dca189c3a1ace3b767492fa214d |
POSTCARD | BSC | 0xcfa332e7a7994d41cc52a72bbabd7f04c4f15bb0 |
wNCSHIP | Polygon | 0x126519a9070fe4b4e005c86a55edaa0750801fa9 |
wNC | Polygon | 0xc30dedd81fe3cd756bffee41199e86b0c3b10218 |
wZUKINFT | Polygon | 0x945af2f45290c2a8ef4f651f35ae2413bee8e4b7 |
E4T | Polygon | 0x374155a0c6ed23046f4f17a0b58995cace6f5bce |
T2WEB | Polygon | 0x75d0bf98e2dbf8248c2545e4fbbb1c0247ef7508 |
LCC | Polygon | 0x491f8d040e90a8a9681f6afe9dfb114f2dbb960c |
PDLS | Polygon | 0xb89d3d7ca23a34ca75f648072ec762c857ee0efd |
LGC | Polygon | 0x84c19001c1c75ef1a869023843d4865f231a3ede |
newNft | Polygon | 0x24fd77efa4e0a7629154dec2d18a82af2ba5126e |
LPCP | Polygon | 0x57dc9bb707c687c056e510a0e7b3a8b98e7bec2a |
Hello, i’m new here on ethmagicians
I’m actually wondering if the final stage means “this implementation = standard” or if the just the interface should be considered as such, thus not setting in the stone the implemention given in gitHub
Not sure if I should discuss in a different EIP or the discusssion on the implementation goes on even when the EIP is marked as “final”.
Anyway i’ll make my question here
Wouldn’t an implementation like this just fix the usership problem while enabling non-custodial protocol to handle 4907 or you can see major iussues doing like:
- When the usership expires returns the ownerOf() instead of address 0.
function userOf(uint256 tokenId) public view virtual override returns(address){
if( userExpires(tokenId) >= block.timestamp){
return _users[tokenId].user;
}
else{
return address(ownerOf(tokenId));
}
}
- Prevent usership to be set again before the current expires.
By allowing setUser to be changed while user is not expired, it becomes mandatory to have an intermediary contract to own the NFT, to prevent the real owner to call setUser() or transfer triggering the delete info[tokenId]
This makes possible for a renter to delegate a contract to setUser instead, thus not requiring custody. In this scenario the user is guaranteed to retain usership from start to end.
function setUser(uint256 tokenId, address user, uint256 expires) public virtual override {
require(_isApprovedOrOwner(msg.sender, tokenId), "ERC4907: transfer caller is not owner nor approved");
require(userOf(tokenId) != ownerOf(tokenId), "ERCXXX: user can't be set before expiration");
UserInfo storage info = _users[tokenId];
info.user = user;
info.expires = expires;
emit UpdateUser(tokenId, user, expires);
}
- Don’t override
_beforeTokenTransfer
anymore
At this point if the user paid his interest for a rental, transfer shoudn’t delete info at all and let third party markets to mark the item as rented using IERC4907.
It will then be on buyer to evaluate wheter or not the deal is convienent or not to him. Think it as a bare ownership more or less
Even tho I’m not sure if this discussion is over or not, i would like to contribute with my opinion. Nice work so far
Wait, whether it sounds not-fair? I am the owner of this NFT(Azuki), and I rental the ownership to “many” people.
nice nice feedback!!!
The status
field of an EIP only describes the document itself and says nothing about the ecosystem or whether people are actually using the EIP. In short, Final
here means: EIP-4907 will not change besides errata and text formatting.
The implementation of EIP-4907 is a reference implementation, you can do a different implementation.
We removed _beforeTokenTransfer
in our newest implementation:
override _burn
function
function _burn(uint256 tokenId) internal virtual override {
super._burn(tokenId);
delete _users[tokenId];
emit UpdateUser(tokenId, address(0), 0);
}
So if we want to make Collateral free Rental marketplace then how this protocol help us.
According to description of erc 4907 it provides user roles not the ownership but how renter use this nft in games without ownership.
In short my question is how renter use these rented nfts in which games that are not supporting user roles ?.I think this protocol only support the owner roles but not the user roles?
If the games use ERC-4907 NFT, they should support the user roles.
How to rent already deployed ERC721? This standard does not cover this use case, does it?
@dadabit we are working on an standard that enables role management for immutable NFTs. It also supports an arbitrary number of roles. Check it out: EIP-7432: Non-Fungible Token Roles
ERC4907 has been applied by researchers in dynamic spectrum sharing.
L. Ye et al ., “Dynamic Spectrum Sharing Based on the Rentable NFT Standard ERC4907,” 2024 13th International Conference on Communications, Circuits and Systems (ICCCAS) , Xiamen, China, 2024, pp. 503-507, doi: 10.1109/ICCCAS62034.2024.10652863.
[image]