WEB3DEV

Cover image for Como Construir NFTs ERC-721 com o IPFS
Fatima Lima
Fatima Lima

Posted on • Atualizado em

Como Construir NFTs ERC-721 com o IPFS

Sumário
  1. Usando Open Zeppelin, Truffle e Pinata
  2. Verificabilidade
  3. Começando
  4. Escrevendo um Contrato Inteligente
  5. Acrescentando Ativos ao IPFS
  6. Reunindo tudo isso

Usando Open Zeppelin, Truffle e Pinata

Usando Open Zeppelin, Truffle e Pinata

O padrão ERC-721 deu origem ao mercado de tokens não fungíveis (NFT) na Ethereum. ERC-721 é um padrão para a criação de um NFT - uma forma complicada de dizer "itens únicos". Qualquer coisa única pode ser um NFT. Uma casa, um cartão de beisebol, uma peça de arte, etc. Mas o poder não está no item apenas em ser único e digitalizado. O poder está na possibilidade de verificação. É aí que brilha o padrão ERC-721.

O problema da criação dos tokens ERC-721 vem do armazenamento dos ativos subjacentes. As blockchains não são boas para o armazenamento de grandes pedaços de dados. Em 2017, Jamila Omaar da Interplanetary Database estimou que o custo do armazenamento de 1GB de dados na Ethereum seria superior a 4 milhões de dólares.

Em geral, o custo para armazenar dados na Ethereum é de aproximadamente 17.500 ETH/GB, ou cerca de US$ 4.672.500 a preços atuais.

Então, se sabemos que o custo de armazenamento dos ativos vinculados a um NFT é muito alto para usar uma blockchain, quais são as alternativas? Poderíamos armazenar os ativos utilizando o tradicional armazenamento em nuvem. O S3 da Amazon e o Azure da Microsoft fornecem soluções de armazenamento baratas. Entretanto, o armazenamento tradicional em nuvem, como o conhecemos, tem uma grande deficiência.

Não é verificável criptograficamente.

Verificabilidade

O principal objetivo de um NFT é a verificação e o controle digital do que pode ser um ativo físico ou digital. Se não pudermos verificar o próprio ativo subjacente de maneira semelhante à verificação da propriedade do token que representa o ativo, perdemos a noção do objetivo final.

A solução para ambos os problemas é o IPFS. O IPFS é uma rede de armazenamento distribuído. Funciona de forma semelhante ao armazenamento em nuvem. Você faz uma solicitação de conteúdo e esse conteúdo é retornado. Entretanto, a grande diferença é que o conteúdo é armazenado por meio de uma rede global de provedores de armazenamento. O IPFS potencializa uma ferramenta chamada endereçamento de conteúdo. Isto significa que em vez de você fazer uma solicitação para aquele centro de dados em Ohio para um pedaço de conteúdo, você faria uma solicitação PARA o próprio conteúdo. Ele pode estar localizado em Ohio. Ele pode estar localizado mais perto. Com a “endereçabilidade” do conteúdo, você não precisa mais confiar em um único local para a recuperação do conteúdo. Isto é muito mais eficiente para blockchains globais.

O IPFS também se encarrega da verificabilidade para nós. Como todo o conteúdo é definido e armazenado com base no próprio conteúdo, caso um pedaço de conteúdo seja adulterado ou alterado, teríamos um desajuste ao tentar verificar o conteúdo e saber que ele está errado. Vamos deixar isto um pouco mais claro com um exemplo simples:

Alice armazena uma foto de um gato no IPFS e essa foto de gato é representada por um identificador de conteúdo. Para simplificar, digamos que o identificador é "C".

Bob pede aquela foto do gato e depois desenha um bigode naquele pobre gato. Quando Bob carrega sua foto, ele não terá mais o mesmo identificador. Como ele mudou os dados subjacentes (de gato para gato com bigode), o identificador do Bob pode ser "M".

Se Bob tentasse passar sua foto como sendo de Alice, qualquer um que se preocupasse em verificar, saberia que ele estava mentindo. O identificador de Alice não corresponde ao de Bob e, portanto, a imagem que Bob está tentando passar como a de Alice é comprovadamente falsa.

Acho que você pode começar a ver como isto é útil ao tentar verificar ativos digitais como NFTs. Então, com isso em mente, vamos ver como podemos criar um NFT e armazenar o ativo associado no IPFS.

Começando

Para este tutorial, você vai precisar de algumas coisas:

  • Um bom editor de texto
  • O IPFS instalado
  • Ganache — blockchain local da Ethereum—instalada
  • Truffle instalado
  • NodeJS instalado
  • Chave API do Pinata

Você pode escolher qualquer editor de texto que desejar. Eu prefiro Visual Studio Code, mas isso é com você.

Uma vez que você tenha escolhido seu editor de texto, vamos nos certificar de que o IPFS esteja instalado. Siga as instruções diretamente do IPFS aqui.

Você poderá pegar o Truffle e o Ganache do mesmo lugar, já que eles estão no Truffle Suite completo.

Se você ainda não tem o Node.js em sua máquina, você terá que fazer download dele também. Você pode encontrar a versão mais recente aqui.

Finalmente, você quer ter certeza de que seu precioso ativo para o qual você cria um NFT está permanentemente armazenado no IPFS. Isto pode ser feito executando seu próprio nó IPFS ou utilizando o chamado Serviço de Fixação IPFS. Para simplificar, vamos replicá-lo através do serviço de fixação do Pinata. Cadastre-se aqui para obter uma conta.

Escrevendo um Contrato Inteligente

Vamos colocar uma rápida isenção de responsabilidade aqui. Eu não sou um desenvolvedor profissional de contratos inteligentes. Sei o suficiente para ser arriscado e no mundo da blockchain, arriscado pode ser igual a perder dinheiro. Portanto, tenha cuidado, faça suas pesquisas, e certamente encontre as melhores práticas. Este guia pretende ser um ponto de partida educacional e provavelmente há maneiras melhores de fazer o que estou mostrando aqui.

Os tokens ERC-721 são regidos por contratos inteligentes. Felizmente, o pessoal no OpenZeppelin garante que você escreva bons contratos com facilidade. Usaremos o contrato ERC-721 deles para nos ajudar a começar.

Primeiramente, em seu terminal, crie uma nova pasta de projeto.

mkdir mySpecialAsset && cd mySpecialAsset
Enter fullscreen mode Exit fullscreen mode

Em seguida, inicializaremos nosso diretório de projetos usando o NPM.

npm init -y
Enter fullscreen mode Exit fullscreen mode

Agora, podemos utilizar o Truffle para inicializar nosso projeto de contrato inteligente.

truffle init
Enter fullscreen mode Exit fullscreen mode

Agora, precisamos ter acesso a esses doces, doces contratos Open Zeppelin. Para isso, instalaremos a biblioteca para Solidity do Open Zeppelin desta forma:

npm install @openzeppelin/contracts
Enter fullscreen mode Exit fullscreen mode

Precisaremos pensar em um bom nome para o nosso token. Vou chamar o meu de UniqueAsset porque estamos garantindo que os NFTs devam ser associados a ativos subjacentes únicos. Você pode chamar seu contrato do que quiser. Queremos hospedar o contrato em uma pasta de contratos, então criaremos nosso arquivo desta forma:

touch contracts/UniqueAsset.sol
Enter fullscreen mode Exit fullscreen mode

Agora podemos abrir esse arquivo e começar a trabalhar. Precisamos especificar a versão (ou versões) do Solidity que sejam compatíveis com nosso contrato. Também precisaremos importar as estruturas do contrato do Open Zeppelin. E podemos começar a implementar o contrato em si. Vamos dar uma olhada:

pragma solidity ^0.6.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Counters.sol"
contract UniqueAsset is ERC721{
 constructor() public ERC721("UniqueAsset", "UNA") {}
}
Enter fullscreen mode Exit fullscreen mode

Precisamos especificar a versão do Solidity que estamos usando. Neste caso, estou usando 0.6.0. Você pode garantir que o Truffle compile seu contrato usando a versão correta do Solidity, editando o arquivo truffle-config.js. Estou importando dois contratos do Open Zeppelin: ERC721 e Counters. O contrato Counters está apenas nos ajudando a incrementar as identificações do token após a emissão. Finalmente, no construtor de nosso contrato, estamos definindo o nome e o símbolo do nosso token. Mais uma vez, você pode definir isto como quiser.

Vamos precisar acrescentar alguma lógica em nosso contrato. Vamos fazer isso agora.

Primeiro, vamos pensar no que estamos tentando fazer aqui. Queremos emitir NFTs para ativos específicos. Queremos que esses ativos sejam verificáveis tanto quanto queremos que a propriedade seja verificável. Portanto, algumas coisas que precisamos considerar aqui:

  1. Queremos associar o NFT com um identificador de conteúdo IPFS (hash).
  2. Não queremos nunca cunhar (ou criar) um NFT que mapeie para o mesmo hash IPFS de outro NFT.

Vamos começar definindo variáveis em nosso contrato que usaremos para ajudar a controlar os dois aspectos acima. Acima de seu construtor no código do contrato, adicione o seguinte:

using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
mapping(string => uint8) hashes;
constructor() public ERC721("UniqueAsset", "UNA") {}
...
Enter fullscreen mode Exit fullscreen mode

Estamos usando Counters para nos ajudar a incrementar os identificadores para os tokens que cunhamos. Estamos também criando uma variável _tokenIds para acompanhar todos os tokens que emitimos. E finalmente, para nossas variáveis de nível superior, estamos criando um mapeamento para os hashes IPFS associados aos tokens. Isto ajudará a evitar a emissão de tokens mapeados para um hash anteriormente associado a outro token.

Seu contrato deve agora ter este aspecto:

pragma solidity ^0.6.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
contract UniqueAsset is ERC721 {
using Counters for Counters.Counter;
 Counters.Counter private _tokenIds;
 mapping(string => uint8) hashes;
constructor() public ERC721("UniqueAsset", "UNA") {}
}
Enter fullscreen mode Exit fullscreen mode

Precisamos acrescentar um método ao nosso contrato que nos permitirá cunhar um NFT para um hash IPFS específico, se ainda não foi cunhado um token para esse hash. Faremos isso logo abaixo de nosso construtor.

function awardItem(address recipient, string memory hash, string memory metadata)
 public
 returns (uint256)
{
 require(hashes[hash] != 1);
 hashes[hash] = 1;
 _tokenIds.increment();
 uint256 newItemId = _tokenIds.current();
 _mint(recipient, newItemId);
 _setTokenURI(newItemId, metadata);
 return newItemId;
}
Enter fullscreen mode Exit fullscreen mode

Há muita coisa acontecendo aqui, então vamos caminhar linha por linha. Nossa função tem dois parâmetros: Uma variável de endereço chamada recipient, uma variável de string chamada hash e uma variável de string chamada metadata. A variável de endereço é o endereço da carteira da pessoa que receberá o NFT. A variável de string para o hash é o hash IPFS associado ao conteúdo para o qual estamos criando o NFT. E a variável de string para metadatadeve se referir a um link para os metadados JSON do ativo. Os metadados podem incluir o nome do ativo, um link para uma imagem referenciando o ativo ou qualquer outra coisa que você queira.

Então, depois de definirmos nossa função, estamos tornando-a public. Isto só significa que ela pode ser chamada de fora do contrato inteligente. Também estamos definindo o valor de retorno da nossa função para ser do tipo uint256.

Dentro da função, estamos usando a função require incorporada do Solidity para rejeitar automaticamente a chamada de contrato se o hash já foi usado para cunhar um NFT antes. Você notará que estamos verificando se nossos mapeamentos de hashes tem um hash correspondente ao inteiro 1. Se assim for, esse hash foi utilizado.

Se o hash não tiver sido usado, adicionamos o hash passado através de nossa função ao nosso mapeamento de hashes e definimos seu valor para 1.

Finalmente, nós incrementamos nossa variável _tokenIds por que estamos a ponto de criar um novo NFT e cunhamos nosso token, devolvendo o identificador do token.

Isso foi muito. Então vamos resumir rapidamente sem nos basearmos no código. Nosso contrato agora inclui o endereço da carteira de uma pessoa na Ethereum e um hash IPFS. Ele verifica se o hash não corresponde a um NFT previamente cunhado. Se tudo estiver correto, um novo NFT é criado especificamente para esse hash IPFS.

Ok, nós escrevemos nosso contrato. E agora?

Vamos compilá-lo e implantá-lo. Lembre-se que eu pedi para instalar o Ganache. Vamos usar isso agora. Inicie o Ganache ou através do ganache-cli ou usando o cliente desktop (eu prefiro o cliente desktop).

Se você der uma olhada dentro de seu diretório de projetos, você verá uma pasta chamada migrations. Precisamos criar um novo arquivo de migração. Estes arquivos são muito específicos e, neste caso, precisamos que ele seja executado após o arquivo de migração existente que foi criado por padrão para você. Para garantir que isso aconteça, nomearemos nosso arquivo de migração com um 2 no início. Vá em frente e chame sua nova migração de 2-deploy-contract.js. Dentro desse arquivo, adicione o seguinte:

var UniqueAsset = artifacts.require("UniqueAsset");
module.exports = function(deployer) {
 deployer.deploy(UniqueAsset);
};
Enter fullscreen mode Exit fullscreen mode

Substitua as referências para UniqueAsset pelo que quer que você esteja chamando de seu contrato.

Uma vez feito isso e salvado, em seu terminal, dentro do diretório do projeto, execute:

truffle compile
Enter fullscreen mode Exit fullscreen mode

Supondo que você não tenha cometido nenhum erro, seu contrato foi compilado e agora pode ser implantado. Basta executar:

truffle migrate
Enter fullscreen mode Exit fullscreen mode

Se der algum erro, pode ser necessário configurar manualmente a porta em que o Ganache esteja funcionando. O cliente desktop torna isso fácil de encontrar. Em seu arquivo truffle-config.js, você poderá encontrar a seção networks e configurar adequadamente a porta da rede de desenvolvimento.

Se tudo correu bem, você acabou de implantar seu contrato inteligente NFT. Aqui parece uma boa oportunidade para lembrá-lo que eu não sou um expert em desenvolvimento de contratos inteligentes. Certamente há melhores práticas que eu perdi e que você mesmo deveria pesquisar se está criando contratos inteligentes.

Você pode testar seu contrato, escrevendo testes no Truffle para ele, ou você pode ir até o Remix e colar ali seu contrato e executar testes. Se você o testar, notará que pode cunhar um NFT associado a um hash IPFS, mas se você tentar cunhar outro NFT para esse mesmo hash, a chamada ao seu contrato falhará.

Agora que cuidamos do contrato inteligente, precisamos colocar nosso ativo subjacente no IPFS e garantir que ele esteja disponível quando chegar a hora de cunhar um NFT associado a ele.

Acrescentando Ativos ao IPFS

Vamos usar o Pinata para adicionar nosso ativo ao IPFS e garantir que ele permaneça fixado. Também adicionaremos nossos metadados JSON ao IPFS para que possamos passá-los para o contrato do nosso token. Portanto, entre na conta que você criou anteriormente no Pinata. Na parte superior direita, clique no menu da conta e escolha Account. Lá você poderá ver sua chave API. Você pode passar o mouse para ver seu segredo API.

Copie ambos, pois vamos usá-los em nosso código para carregar nosso arquivo de ativos.

Clique em Nova Chave API e faça suas seleções. Para mim, acho que só quero que esta chave seja usada uma vez e só quero que ela tenha acesso ao endpoint pinFileToIPFS já que é assim que vamos colocar nosso arquivo de ativos no IPFS.

Uma vez que você tenha sua chave API e a chave secreta, você pode escrever algum código para adicionar seu arquivo de ativos ao IPFS. Se você estiver se sentindo cansado depois de todo aquele trabalho do contrato inteligente, não se preocupe porque isto vai ser super simples. Na verdade, se você quiser pular o código por completo, o Pinata tem um recurso conveniente de upload na Interface do Usuário (UI).

No seu editor de código, crie um novo arquivo chamado uploadFile.js. Isto pode ficar no mesmo diretório onde você criou seu contrato inteligente. Antes de escrevermos nosso código, seria bom ter seu arquivo de ativos pronto. Apenas certifique-se de que ele esteja gravado em algum lugar no computador que você está usando. Para mim, eu vou carregar uma pintura que meu filho fez:

Pintura de ilustração

Agora que temos nosso ativo salvo e pronto para ser carregado, vamos escrever nosso código. Precisamos de duas dependências para que isso seja mais fácil para nós. Em seu terminal, na raiz de seu projeto, execute o seguinte:

npm i axios form-data
Enter fullscreen mode Exit fullscreen mode

Agora, no arquivo uploadFile.js, adicione o seguinte:

const pinataApiKey = "SUACHAVEDEAPI";
const pinataSecretApiKey = "SUACHAVESECRETA";
const axios = require("axios");
const fs = require("fs");
const FormData = require("form-data");
const pinFileToIPFS = async () => {
 const url = `https://api.pinata.cloud/pinning/pinFileToIPFS`;=
 let data = new FormData();
 data.append("file", fs.createReadStream("./pathtoyourfile.png"));
 const res = await axios.post(url, data, {
   maxContentLength: "Infinity",
   headers: {
     "Content-Type": `multipart/form-data; boundary=${data._boundary}`
     pinata_api_key: pinataApiKey,
     pinata_secret_api_key: pinataSecretApiKey,
   },
 });
 console.log(res.data);
};
pinFileToIPFS();
Enter fullscreen mode Exit fullscreen mode

Para executar o script neste arquivo, basta executar o seguinte comando a partir do terminal:

node uploadFile.js
Enter fullscreen mode Exit fullscreen mode

A partir do carregamento bem sucedido, você terá um resultado como este:

{
 IpfsHash: 'QmfAvnM89JrqvdhLymbU5sXoAukEJygSLk9cJMBPTyrmxo',
 PinSize: 2936977,
 Timestamp: '2020-12-03T21:07:13.876Z'
}
Enter fullscreen mode Exit fullscreen mode

Esse hash é a representação verificável de seu ativo. Ele aponta para seu ativo na rede IPFS. Se alguém adulterasse seu ativo e o mudasse, o hash seria diferente. Esse hash é o que deve ser usado quando se cunham os NFTs através de nosso contrato inteligente. Qualquer provedor IPFS que ofereça um portal público será capaz de exibir o conteúdo para você agora.

O Pinata possui um portal e você pode visualizar o ativo que você acabou de carregar aqui.

A última coisa que precisamos fazer é criar um arquivo JSON que represente nossos ativos e seus metadados. Isto facilita qualquer serviço para o qual você queira listar seus ativos para mostrar os metadados apropriados. Vamos criar um arquivo JSON simples como este:

{
 "name":"A Arte do Meu Filho",
 "hash": "QmfAvnM89JrqvdhLymbU5sXoAukEJygSLk9cJMBPTyrmxo",
 "by": "Justin Huner"
}
Enter fullscreen mode Exit fullscreen mode

Você pode adicionar os metadados que quiser, mas é importante incluir o hash. Esta é a referência ao ativo real. Agora, carregue este arquivo da mesma forma que você carregou o arquivo de ativos usando o Pinata. Quando você receber de volta o hash IPFS para os metadados, mantenha isso à mão. Você vai precisar disso ao criar o token.

Lembra que o contrato inteligente inclui uma string de metadados? Essa string será a URL do IPFS para os metadados. Você vai construí-lo assim:

ipfs://HASH_DOS_SEUS_METADADOS
Enter fullscreen mode Exit fullscreen mode

Então, para resumir, você passará três itens para a função de contrato inteligente que criamos anteriormente:

  • Endereço do destinatário
  • Hash do Ativo
  • URL dos Metadados

Reunindo tudo isso

As NFTs são uma melhoria importante na forma como lidamos com a propriedade de todos os tipos de produtos. Eles são facilmente transferíveis e simplificam o processo de criação da propriedade e de comprovação da propriedade. A peça que falta, porém, tem sido a verificação da propriedade de um item específico.

Ao salvar ativos no IPFS e associar o hash IPFS com o NFT do ativo, podemos expandir a propriedade verificável do ativo para a verificação da própria validade do ativo.

O Pinata ajuda a racionalizar este processo, facilitando o armazenamento de ativos no IPFS.

Feliz fixação!

Este artigo foi escrito por Justin Hunter e traduzido por Fátima Lima. Seu original pode ser lido aqui.


Abrace a oportunidade de elevar sua jornada de desenvolvimento para um nível superior. Construir NFTs em ERC-721 com IPFS é apenas o começo; os builds incríveis da WEB3DEV representam a chave de entrada para o emocionante cenário web3. 🚀🧑‍💻

Não perca tempo, 👉inscreva-se👈 agora mesmo e comece a desbravar o universo Blockchain!
 
Seja também WEB3DEV!


Outros artigos sobre NFTs.

Top comments (0)