Como criar NFT usando Solidity e o padrão ERC-721

Cripto

Como criar NFT usando Solidity e o padrão ERC-721

Luiz Duarte
Escrito por Luiz Duarte em 21/03/2023
Junte-se a mais de 34 mil devs

Entre para minha lista e receba conteúdos exclusivos e com prioridade

Neste tutorial eu vou lhe ensinar como criar contratos inteligentes (smart contracts) de tokens não-fungíveis (NFT) na blockchain usando a linguagem de programação Solidity e o padrão ERC-721, seguido por todas as redes baseadas em Ethereum (BSC, Avalanche, Polygon, Tron, etc). É importante, antes de avançarmos, que você saiba o que é e para que serve um NFT, então darei uma rápida introdução sobre o assunto, sobre o padrão e depois a programação em si. Fique à vontade para pular etapas conforme o seu domínio do assunto.

Este não é um tutorial de Solidity para iniciantes e é recomendado que o programador já tenha certa familiaridade com outros padrões mais básicos ligados a tokens, como o ERC-20, além da linguagem de programação em si. Você pode aprender o básico de Solidity neste tutorial, e sobre o padrão ERC-20 neste outro aqui.

O que é um Token Não-Fungível (NFT)?

A palavra fungível soa alienígena mas o seu conceito é bem simples de entender. Se preferir, o vídeo abaixo explica esta parte teórica inicial.

Algo fungível é algo consumível e substituível, como o dinheiro. Você gasta ele para usar e uma nota de 100 pode ser trocada por outra, ou até mesmo por várias menores, formando valor equivalente, sem perder qualquer uma de suas propriedades ou valor. Ao usar duas notas de 50 ou uma de 100 para pagar algo, não há absolutamente qualquer diferença prática, você vai ter de pago da mesma forma, quem recebeu tem exatamente o valor que pediu em mãos.

Já algo não-fungível é justamente algo que não é substituível. Na verdade você até pode substituir, mas a troca NUNCA será 100% equivalente. Por exemplo um imóvel ou um carro. Por mais que você troque por outro de mesmo valor de mercado, a troca sempre irá possuir diferenças, seja na localização do imóvel, seja no estado de conservação do carro ou nas características dos bens. O mesmo vale para obras de arte, bilhetes de eventos, colecionáveis com edição limitada, etc.

No mundo digital também temos itens (tokens) não-fungíveis. Se eu crio uma arte digital, ela é tão não-fungível quanto um quadro a óleo, certo? Se eu crio uma videoaula, ela também é não-fungível, certo? Note que o conceito de fungibilidade não tem a nada a ver com quanto vale um item ou quão difícil foi de fazer ele, já que valor é algo subjetivo principalmente para itens artísticos e depende principalmente da raridade do item.

Mas Luiz, será que existem coisas não-fungíveis quando tudo são bits e bytes? Se eu posso facilmente criar cópias de itens com Ctrl+C e Ctrl+V, como saber qual é o original ou quem é o dono de fato de um item?

Aí entram os contratos, tanto no mundo físico, quanto no digital.

Curso FullStack

O que é um contrato de NFT?

Quando estamos no mundo físico e queremos a propriedade de algo, o que fazemos? Além de pagarmos por aquele bem, se for um bem de valor que justifique, é exigido um contrato, firmado em cartório, cedendo a propriedade do antigo dono, para a gente. É assim com carros, casas, ações de empresas…certo?

O mesmo vale quando realizamos grandes feitos e queremos comprovar que o fizemos. Emitimos certificados para nossas graduações, nosso recordes, experiências de trabalho e muito mais.

E no mundo digital? Se eu tenho um item digital como uma arte que criei, ou um personagem em um jogo, e quiser comprovar a posse ou até transferi-la para outra pessoa, como fazemos? Nada impede que eu copie um JPG de uma foto que alguém tirou e diga que é minha, mas também nada impede que eu crie um diploma de medicina agora e imprima na impressora e diga que é meu, certo? A diferença do real para a cópia é a autenticidade, a comprovação da posse baseada na confiança em cima de um contrato, seja ele físico ou digital.

Até a invenção das NFTs não existia uma maneira padronizada para fazer o registro de propriedade digital no mundo online. Não quer dizer que ela não era feita de alguma forma (eu mesmo tenho ebooks registrados), mas a criação e popularização das NFTs e dos padrões para criá-los, permitiu que esse mercado surgisse de fato e explodisse em 2021 de tanto sucesso. Um NFT nada mais é do que um certificado, um contrato registrado na blockchain de que um determinado item digital é de propriedade de alguém, representado por uma carteira de criptomoedas.

Sendo a blockchain um grande livro de registros imutável e distribuído, podemos eliminar a necessidade de registrar nossos certificados de itens digitais em um cartório analógico e transferimos essas responsabilidade para os algoritmos, assim como já o fazemos com o dinheiro virtual que chamamos de criptomoedas. Isso tem inúmeras vantagens, como o acesso público ao certificado comprovando que é seu, o baixo custo já que eliminamos um terceiro de confiança e principalmente, a facilidade na transferência de propriedade, o que justamente permitiu o surgimento de marketplaces gigantescos de NFTs como a OpenSea e economias em tornos de jogos como Axie Infinity e CryptoKitties.

Mas então como eu crio um contrato de NFT para comprovar a posse de algo que eu criei?

Curso Node.js e MongoDB

O padrão ERC-721

Tecnicamente falando você não precisa programar o contrato da sua NFT se não quiser. Plataformas como as da OpenSea e da Binance permitem que itens populares como imagens, áudios, etc possam facilmente ser colocados na blockchain e à venda em seus marketplaces. Além disso, essas plataformas permitem que após você mintar (cunhar) o seu token, que mesmo que ele seja revendido no futuro, que você continua ganhando comissões a cada venda e para manter estes serviços essas plataformas também cobram pequenas comissões.

Então se você é um artista e apenas quer criar a sua coleção NFT, não há razão para se preocupar com padrões ou programação. Isso passa a fazer sentido se você quer empreender neste mercado, quer criar plataformas, quer trabalhar como programador em empresas de tokenização (que pegam propriedades físicas e registram na blockchain) e por aí vai. Se esse for o seu caso, a sua primeira missão é entender os padrões de contratos NFT mais usados no mercado, sendo o primeiro e principal deles a ERC-721, que você confere a documentação completa neste link.

Assim como outros padrões ERC (Ethereum Request for Comments), o 721 define funções e eventos para que os NFTs sejam registrados de maneira padronizada em redes EVM-based (Ethereum e compatíveis). Isso implica em, por exemplo, utilizarmos a linguagem Solidity na sua programação e é o que faremos aqui. Primeiro, apenas vamos dar uma rápida olhada no que é definido pelo padrão para entendermos melhor o que vamos programar.

São 9 funções e 3 eventos:

balanceOf(address owner) external view returns (uint256)

Nesta função você passa o endereço de uma carteira e o contrato vai lhe retornar quantos NFTs deste contrato aquele endereço possui. Isso porque você pode criar um contrato de um item singular e raro ou um contrato que controla vários itens (uma coleção NFT), cada um com seu id único.

ownerOf(uint256 tokenId) external view returns (address)

Esta função é o oposto da anterior. Com ela você diz qual o NFT você está procurando o dono (pelo id do NFT) e ela lhe retorna o endereço do mesmo, se houver.

safeTransferFrom e transferFrom(address from, address to, uint256 tokenId) external payable

Aqui nós temos 3 funções diferentes para fazer a mesma coisa: transferir a propriedade de um NFT (tokenId) de uma carteira (from) para outra (to). A diferença entre transferFrom e safeTransferFrom é que a segunda (que possui duas versões, uma com dados customizados e outra sem) faz algumas validações adicionais, tornando-a mais segura mas mais custosa do ponto de vista de gás.

Importante frisar também que o parâmetro from deve ser igual ao msg.sender da transação ou algum outro endereço pré-aprovado (falaremos mais adiante) ou dará erro. Além disso, vale notar que esta é uma função payable, ou seja, ela recebe junto da transação um valor monetário que será pago pela transferência de propriedade, ficando junto ao saldo do contrato (via de regra) que mais tarde pode ser transferido para a carteira do dono do contrato.

approve(address operator, uint256 tokenId) external payable

Esta função serve para delegar o controle de um NFT (mas não sua popriedade) a um endereço de carteira específico, chamado de operador ou controlador. Isso permitirá a esse controlador aprovado fazer a transferência por conta própria depois. Isso é especialmente útil para marketplaces que vendem NFTs em seu nome e cobram comissões por isso.  Para revogar uma permissão dada, basta aprovar para a carteira “zero” e é importante citar também que quando um NFT é transferido, qualquer aprovação existente deve ser revogada na sua implementação.

setApprovalForAll(address operator, bool approved) external

Esta função é para permitir que você delegue o controle de TODOS os NFTs de um owner a um operador específico. Basicamente o mesmo que a função anterior, mas para todos itens da coleção de uma pessoa. O booleano permite revogar este acesso.

getApproved(uint256 tokenId) external view returns (address)

Esta função retorna quem é o controlador aprovado para uma NFT específica ou zero caso não exista.

isApprovedForAll(address owner, address operator) external view returns (bool)

Esta função retorna se todos os NFTs do owner em questão estão sob controle do operador especificado.

Além destas 9 funções o programador pode criar outras e isso é bem comum aliás. Funções comuns incluem minting e burning dos NFTs, para saque dos fundos do contrato, para metadados dos NFTs e muito mais. Falaremos sobre isso no futuro.

O padrão ERC-721: Eventos

Dependendo da função que for chamada e da situação do contrato, um ou mais eventos podem ser disparados. Eu já expliquei aqui no blog como escutar estes eventos estando conectado à blockchain com EthersJS e Web3.js, Os eventos abaixo são os obrigatórios do padrão ERC-721, devendo ser implementados em todos contratos deste tipo.

event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);

Esse evento é disparado toda vez que um NFT muda de owner, com exceção quando o contrato é criado.

event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);

Esse evento é disparado toda vez que o controlador/operador de um NFT muda, sendo que um endereço zero indica que foi revogado o controle. Quando um NFT muda de dono, além do evento Transfer também é disparado um evento Approval, mudando o controlador/operador para zero.

event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);

O mesmo que o anterior, mas para a aprovação de toda coleção.

Além das funções e eventos obrigatórios existem uma série de itens opcionais ou que fogem do escopo tradicional de implementação. Por exemplo, se você é desenvolvedor de um software de carteira e deseja que a sua carteira possa guardar NFTs, você deve implementar a interface ERC721TokenReceiver descrita na documentação.

Outros pontos opcionais incluem por exemplo os meta-dados de uma NFT, que dão características complementares ao seu id original, algo muito comum já que NFTs costumam ser colecionáveis digitais com imagem, por exemplo. Mas este é um conceito mais complexo que podemos abordar em outro momento. Isso porque agora você deve estar ansioso para codificar seu primeiro contrato NFT, certo?

Começamos a fazer isso neste tutorial.

Olá, tudo bem?

O que você achou deste conteúdo? Conte nos comentários.

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *