NFT Rental Development (ERC-4907)
Before ERC-4907, NFT "rental" was implemented through workarounds: depositing a token into an escrow contract, trusting off-chain agreements, or custom role mappings without a unified standard. Any protocol wanting to use someone else's NFT (game, metaverse, lending) had to build a separate integration for each project. EIP-4907, adopted in 2022, solves this cleanly: it adds a second address — user — with temporary usage rights, leaving the owner untouched.
How ERC-4907 Works
The standard extends ERC-721 with two functions and one event:
interface IERC4907 {
event UpdateUser(uint256 indexed tokenId, address indexed user, uint64 expires);
function setUser(uint256 tokenId, address user, uint64 expires) external;
function userOf(uint256 tokenId) external view returns (address);
function userExpires(uint256 tokenId) external view returns (uint256);
}
Key point: user automatically resets to address(0) after expires. No cron job, no keeper — just a check in userOf():
function userOf(uint256 tokenId) public view virtual returns (address) {
if (uint256(_users[tokenId].expires) >= block.timestamp) {
return _users[tokenId].user;
}
return address(0);
}
owner retains full control: can transfer the token, receive royalties, set a new user. When the token is transferred, user and expires are automatically reset — this protects against the renter retaining rights after the owner changes.
Rental Marketplace Architecture
ERC-4907 itself is just a token-level standard. A complete rental system requires a rental contract on top of it.
Escrow-free vs. Escrow Approaches
Escrow-free (recommended): the owner approves the rental contract via approve(), the renter pays, and the contract calls setUser() on behalf of the owner. The token remains in the owner's wallet throughout the rental. No custodial risk.
Escrow: the token is locked in the contract. Only needed if the owner wants a guarantee not to use the token themselves during the rental — a rare use case.
Rental Order Structure
struct RentalOrder {
address tokenContract;
uint256 tokenId;
address lender;
uint256 pricePerDay; // in wei
uint64 minDuration; // in seconds
uint64 maxDuration;
uint64 deadline; // order validity deadline
bytes signature; // EIP-712 lender signature
}
Off-chain order book (like 0x protocol) + on-chain settlement. Lender signs the order off-chain — no gas costs for listing. Renter calls rent(order, duration) — one transaction, payment + setUser().
Automatic Renewal and Early Termination
ERC-4907 doesn't provide for early rental termination by the renter — expires is immutable after setUser(). For collateral-based rentals (protection against asset damage in games), additional logic is needed: renter deposit + ability for lender to call slash with on-chain proof of breach.
Integration with Games and Protocols
Protocols should replace ownerOf() checks with userOf():
// Before:
require(IERC721(nft).ownerOf(tokenId) == msg.sender, "Not owner");
// After:
address user = IERC4907(nft).userOf(tokenId);
require(user == msg.sender, "Not authorized user");
For backward compatibility with contracts not supporting ERC-4907: a wrapper contract that wraps a standard ERC-721 into ERC-4907. The owner deposits the original token and receives a wrapped version with rental functionality.
What's Included in Development
- ERC-4907 contract (if collection is created from scratch) or wrapper for existing collection
- Rental marketplace contract: off-chain orders with EIP-712 signatures, on-chain settlement
- Pricing logic: fixed price per day, Dutch auction for price decline over time
- Frontend: listing, search for available tokens, one-click rental
- Integration with consumer protocol: replacing
ownerOfwithuserOf
Timeline Estimates
ERC-4907 contract only + basic rental — 2 days. With off-chain order book, frontend, and integration into game contract — 4-5 days.







