NFTs não são apenas uma garantia criptográfica de propriedade de uma imagem (ok, não é uma imagem, eu sei, mas muita gente pensa assim). Com a mesma tecnologia por trás deles, as coisas mudam dependendo de como trabalhamos, interagimos e brincamos com eles.
Estou falando do NFT utilitário, que permite certos privilégios, direitos ou recompensas aos seus proprietários.
Por exemplo, em jogos, o NFT utilitário oferece uma nova abordagem para gerenciar a propriedade de ativos no jogo ou, em espaços sociais, ajuda a garantir acesso seguro a comunidades exclusivas (Bored Ape Yacht Club é um exemplo famoso).
Outro caso de uso pode ser um NFT vinculado a um “ativo físico inteligente alugável” (por exemplo, um armário, uma casa de férias ou um carro), onde o usuário pode enviar comandos (por exemplo, “trancar porta”, “destrancar porta”) depois de provar que ele é o atual proprietário do NFT. Mas cruzaremos essa ponte quando chegarmos a ela, em outro artigo.
Falando em NFT utilitário, em alguns casos, faz sentido que o proprietário e o usuário nem sempre sejam os mesmos. O proprietário do NFT poderia alugá-lo a um usuário por um determinado tempo. O usuário, durante esse tempo, ganha temporariamente os privilégios concedidos pelo NFT, mas não pode transferir sua propriedade.
O estado da arte
Bem, a boa notícia é que existe uma EIP para NFTs alugáveis (EIP 4907) que afirma:
“Este padrão é uma extensão da EIP-721. Propõe uma função adicional (de usuário) que pode ser concedida aos endereços e um tempo em que a função é automaticamente revogada (tempo de expiração). A função de usuário representa a permissão para “usar” o NFT, mas não a capacidade de transferi-lo ou definir usuários”.
Este padrão foi projetado para ser totalmente compatível com o ERC-721 e basicamente apresenta uma interface para implementação do contrato (IERC4907.sol) com os seguintes métodos:
function setUser(uint256 tokenId, address user, uint64 expires)
Este método define o novo usuário e o período de expiração do NFT, se o chamador for o proprietário do NFT ou de um endereço aprovado.
function userOf(uint256 tokenId) external view returns(address);
Isso retorna o endereço do usuário atual do NFT, onde o endereço zero indica que não há usuário ou o período de aluguel expirou.
function userExpires(uint256 tokenId) external view returns(uint256);
O último método retorna o tempo de expiração para o usuário do NFT, onde um valor zero indica “nenhum usuário”.
Observando a implementação de referência (ERC4907.sol), podemos notar como o contrato estende o padrão ERC721 e utiliza um mapeamento entre o NFT e seu eventual usuário com o tempo de expiração.
contract ERC4907 is ERC721, IERC4907 {
struct UserInfo
{
address user; // endereço da função do usuário
uint64 expires; // timestamp unix, expiração do usuário
}
mapping (uint256 => UserInfo) internal _users;
...
Uma possível melhoria
A primeira pergunta que me fiz foi: “Se o título da EIP é Rental NFT (NFT de Aluguel), por que o contrato IERC4907 ou a implementação de referência não possui um método de “aluguel””?
Ok, existe o método setUser, mas com ele, apenas o dono (ou endereço aprovado) do NFT pode definir um usuário. Nesse caso, o processo soa mais como um processo de empréstimo, onde o proprietário empresta os privilégios vinculados a um NFT utilitário a um usuário e, além disso, paga taxas de gás para isso.
O que eu tinha em mente era algo parecido com:
Aqui o Proprietário tem a opção:
- De definir seus NFTs como alugáveis;
- De definir a quantidade de ETH necessária para alugar seus NFTs por um determinado período.
Se o NFT estiver listado como alugável, um Usuário tem a possibilidade de alugá-lo e paga ao Proprietário por isso.
Um usuário que deseja alugar o NFT deve enviar uma transação ao Contrato especificando o NFT que deseja alugar e o período de tempo do aluguel. Além disso, a transação deve ter a quantidade correta de ETH para o período de tempo escolhido.
Vejamos as partes relevantes do código do Solidity:
contract RentableNFT is ERC4907, Ownable
Obviamente, o contrato RentableNFT estende a implementação de referência ERC4907 e o contrato OpenZeppelin Ownable (ok, concordo com você, a extensão Ownable não é estritamente necessária, mas neste contrato, quero que a cunhagem seja permitida apenas ao proprietário do contrato).
uint256 public baseAmount = 1000000000000000; //0.001 ETH
struct RentableItem {
bool rentable;
uint256 amountPerMinute;
}
mapping(uint256 => RentableItem) public rentables;
- baseAmount é o valor padrão a ser pago por um minuto de aluguel;
- RentableItem é uma struct onde se armazena informações sobre rentabilidade e taxas para um determinado NFT;
- rentables é um mapeamento entre NFTs e a struct RentableItem.
function mint() public onlyOwner {
currentTokenId.increment();
uint256 newItemId = currentTokenId.current();
_safeMint(owner(), newItemId);
rentables[newItemId] = RentableItem({
rentable: false,
amountPerMinute: baseAmount
});
}
Por padrão, na função _mint _(cunhagem), o novo NFT criado:
- é cunhado para o proprietário do contrato;
- é definido como não alugável;
- suas taxas são definidas como baseAmount.
function setRentFee(uint256 _tokenId, uint256 _amountPerMinute) public {
require(_isApprovedOrOwner(_msgSender(), _tokenId), "Chamador sem propriedade do token e desaprovado");
rentables[_tokenId].amountPerMinute = _amountPerMinute;
}
Este é o método de configuração para o valor amountPerMinute do NFT. Este método pode ser chamado apenas pelo proprietário (ou endereço aprovado) do NFT.
function setRentable(uint256 _tokenId, bool _rentable) public {
require(_isApprovedOrOwner(_msgSender(), _tokenId), "Chamador sem propriedade do token e desaprovado");
rentables[_tokenId].rentable = _rentable;
}
Este método define o valor booleano de aluguel de um NFT. Este método pode ser chamado apenas pelo proprietário (ou endereço aprovado) do NFT.
function rent(uint256 _tokenId, uint64 _expires) public payable virtual {
uint256 dueAmount = rentables[_tokenId].amountPerMinute * _expires;
require(msg.value == dueAmount, "Valor incorreto");
require(userOf(_tokenId) == address(0), "Alugado");
require(rentables[_tokenId].rentable, "Aluguel desabilitado para o NFT");
payable(ownerOf(_tokenId)).transfer(dueAmount);
UserInfo storage info = _users[_tokenId];
info.user = msg.sender;
info.expires = block.timestamp + (_expires * 60);
emit UpdateUser(_tokenId, msg.sender, _expires);
}
Aqui está o método principal de aluguel, que:
- calcula o valor devido pelo aluguel do NFT;
- verifica se o valor enviado está correto;
- verifica se o NFT já não está alugado por outra pessoa;
- verifica se o NFT foi alugado por seu proprietário;
- transfere o valor devido ao proprietário do NFT;
- atualiza informações sobre o usuário e o período de expiração.
O contrato proposto está disponível no github, neste link.
Artigo original escrito por Gold dev. Traduzido por Paulinho Giovannini.
Oldest comments (0)