WEB3DEV

Cover image for Como Escrever um Contrato Inteligente de um NFT Alugável com ERC4907
Paulo Gio
Paulo Gio

Posted on

Como Escrever um Contrato Inteligente de um NFT Alugável com ERC4907

https://miro.medium.com/max/1100/1*i5HODYwSaY1ysluC3jFuvw.webp

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)
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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;
 ...
Enter fullscreen mode Exit fullscreen mode

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:

https://miro.medium.com/max/640/1*ua-G2p9XK8tHx0IT4vdAww.webp

Aqui o Proprietário tem a opção:

  1. De definir seus NFTs como alugáveis;
  2. 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
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode
  • 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
   });
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)