This document will talk about the Rentals Smart Contract on the Ethereum network that enables the rental of Land (Parcels and Estates) and other compatible ERC721 assets.
Rentals are a great way to earn passive income over assets. This will benefit owners that currently hold Land but don't know or want to build on it, creators that might find it expensive to buy Land and deploy scenes on it, and the whole of Genesis City as more and more creators will have access to populate and give it more life.
Being able to rent your Land was something the community has been asking for a while now. Rental solutions exist, from dapps and smart contracts deployed by the community to rentals being handled off-chain in a peer-2-peer manner.
However, unofficial protocols and off-chain solutions might not take into consideration the best security practices to protect users as well as Decentraland's most precious assets. That is why Decentraland has developed its protocol.
The IERC721Rentable interface provides a glimpse of what kind of assets are compatible with the Rentals contract.
The contract of the asset to be rented must be
ERC721 compliant. Moreover, it has to
expose an extra function setUpdateOperator(uint256 tokenId,address)
, like the one
found on the
LANDRegistry
and
EstateRegistry
contracts.
The required methods are:
ownerOf
Checks if the asset has to be transferred to the Rentals contract after executing a Rental.
Checks if the asset was transferred "unsafely" using the
ERC20.transferFrom
function to the Rentals contract.
safeTransferFrom
Transfer the asset from the original owner to the Rentals contracts when the rental is executed.
Transfer the asset back to the original owner when they claim it back after the rental period is over.
supportsInterface
Checks that the asset's contract implements the
verifyFingerprint(uint256, bytes memory)
method.
setUpdateOperator
Define the address that will be able to work on the asset when the rental is executed. In the case of Land, for example, this method will determine the address that can deploy scenes to it.
There are 2 extra functions that the Rentals contract may call from the rented asset but they depend on the implementation and might not be required like the previous ones.
verifyFingerprint
Only when supportsInterface
returns that the asset contract implements this
method, it will be called. This method validates that composable assets, such as Estates, have
not been modified before the rental is executed, preventing tenants from renting an asset
different than expected.
setManyLandUpdateOperator
This function is a workaround for setting update operators for parcels inside an Estate. Any other asset does not need to implement (unless a similar requirement is needed).
Listings and Offers are data structures that contain the information required to execute a rental. A Listing is created by the owner of an asset that wants to list an asset for rent given a set of conditions. An Offer is created by any user that wants to rent a certain asset for a given set of conditions.
Listing
address(0)
only the target can accept it.
Offer
signer
will be given the update
operator role.
Listings and Offers are a fundamental part of being able to rent an asset, however, this kind of data does not need to be tracked on-chain for a rental to occur.
To prevent users from spending money on creating/updating/deleting them, they are handled off-chain by making use of EIP712 for hashing and signing typed structured data. The data and its signature are then stored in a place where the consumer can access it and initiate a rent.
The diagram shows the flow of a lessor creating and signing a Listing that is then stored off-chain. The tenant fetches both to start a new rental.
Listings and Offers have an indexes
property that is an array composed of 3
integers. Each one of these integers represents a verification index that the Rentals contract
will use to verify that the Listing/Offer is still valid.
Any of these indexes can be updated at any time to invalidate signatures. Each index is updated differently and is in charge of invalidating signatures in different ways.
Contract Index
This index can be updated by the owner of the Rentals contract. Updating this index with
bumpContractIndex()
will invalidate all signatures created with the previous
value. It is intended to be used in case there is a signature leak from the off-chain
signature storage to protect users.
Signer Index
Each address has its signer index. Updating this index with
bumpSignerIndex()
will update the index of the sender. This is helpful for users
that have lost track of their signatures and want to invalidate them all at once.
Asset Index
Similar to the Signer Index, but for particular assets. Instead of invalidating all signatures
created by an address, a user can call
bumpAssetIndex(address _contractAddress, uint256 _tokenId)
to invalidate all
signatures created for a particular asset.
Executing a rental requires calling particular functions in the Rentals contract with a Listing/Offer and a signature obtained from that data using a private key. The way the data has to be signed is determined by EIP712
Listings and Offers have a property signer
. The value of this property has to
match the recovered signer from the provided signature. If they don't match, it means that the
signature is invalid and the rental transaction will revert.
Account A signs Listing with signer B ❌
Account A signs Listing with signer A ✅
Smart Contract Accounts don't have private keys. This means that the account is unable to sign a Listing/Offer. If a user has a Smart Wallet with an asset they want to rent, it would be impossible to do so this way.
For cases like this, the Rentals contract detects if the provided signer is a Smart Contract
Account that follows the
ERC1271 standard. It calls the
isValidSignature
of the provided signer and delegates the validation. This way,
an Externally Owned Account that has control over a Smart Contract Account such as Smart
Wallets can create Listings/Offers in their name.
EOA A signs Listing with signer B that is a Smart Wallet owned by A ✅
The first time an asset is rented, it is transferred from its original owner to the Rentals contract. This transfer prevents the asset from being operated freely by the lessor while the rental is running. In this case, the contract acts as an escrow that makes sure that only a limited amount of actions can be performed on the asset that are only relevant to renting it.
Rental Ongoing
The tenant can set its update operator as many times as desired. In the particular case of Estates, the tenant can set the update operators of internal parcels as well through the Rentals contract. The lessor is unable to operate the asset in any way.
As long as the tenant and the lessor are the same, a new Listing/Offer can be accepted to extend the rent.
Rental Finished
Once the rental ends, the lessor is the one that can do the previously mentioned actions while the tenant can't do them anymore.
The lessor also has the option to claim the asset back, causing it to be transferred back from the Rentals contract to its original owner.
The lessor can also create new Listings or accept new Offers for the asset without the need of claiming it back. This is called a re-rent. This saves some gas because an extra transaction is prevented.
The Rentals Smart Contract by itself might not be too easy to use, especially because handling signatures is a complex thing.
For the contract to be used successfully, a dapp to facilitate signing and a server to handle these signatures will be required.
The contract design does not require any existing protocol to be updated.
Rentals Smart Contract deployed on the Goerli network on Sep 30, 2022
Rentals Smart Contract deployed on Mainnet on Nov 28, 2022
Rentals feature went live on the Marketplace and Builder with its first executed rental on Dec 05, 2022
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.