Uma maneira mais rápida e eficiente de criar NFTs
Atualmente, as taxas de gas na Ethereum são astronômicas. Faz sentido que as soluções de camada 2 estejam tendo um boom de utilização. No entanto, é importante garantir que os NFTs que iniciam suas vidas em uma solução de camada 2 sejam criados de uma maneira que os torne portáteis e verificáveis.
Uma das soluções de camada 2 mais conhecidas e usadas no espaço Ethereum é a Polygon (antes conhecida como Matic). A Polygon permite a implantação de contratos inteligentes e transações com benefícios e desempenho significativos. Quando é hora de mover ativos da e para a cadeia principal da Ethereum, a Polygon facilita bem isso.
Hoje vamos construir um contrato que cunhará NFTs que são apoiados pelo IPFS. Vamos começar! Você vai precisar de um editor de código, do NodeJS instalado e um pouco de dedicação.
A Configuração
Usaremos o Hardhat para lidar com a configuração e a implantação do nosso projeto. O Hardhat, assim como o Truffle, fornece um conjunto de ferramentas que ajudam a facilitar o desenvolvimento na Ethereum. Como a Polygon é um sistema de camada 2 compatível com a Ethereum EVM (Máquina virtual Ethereum), podemos usar o Hardhat com a Polygon praticamente da mesma maneira que usaríamos com a Ethereum.
Abra o terminal em seu computador e crie uma nova pasta de projeto. Vamos chamá-lo de polygon-nfts
.
mkdir polygon-nfts
Mude para esse diretório e vamos inicializar um novo projeto de exemplo do Hardhat para começar. Na raiz do diretório polygon-nfts
, execute os seguintes comandos:
npm install --save-dev hardhat && npx hardhat
O segundo desses comandos o guiará por algumas opções. Escolha as opções do projeto de exemplo e aceite todas as opções-padrão apresentadas depois disso. Quando esse processo estiver concluído, você terá alguns novos arquivos na pasta do seu projeto. Abra toda a pasta do projeto no editor de código de sua escolha e vamos dar uma olhada no que temos.
Você deve ter um diretório parecido com este:
contracts
-Greeter.sol
scripts
-sample-script.js
test
-sample-test.js
.gitignore
hardhat.config.js
package-lock.json
package.json
Como decidimos usar um projeto de exemplo, temos três pastas que, de outra forma, não teríamos se estivéssemos começando com um projeto em branco:
- contracts
- scripts
- test
A pasta contracts
tem um arquivo Greeter.sol
de exemplo. A pasta scripts tem um arquivo de script simples que obtém o código para o contrato e o implanta. Bem simples. Nosso arquivo de testes testa se nosso contrato pode ser implantado e se pode ser chamado. Coisas bem simples.
Não vamos usar muito isso, mas é um ótimo ponto de partida à medida que entendemos como conectar a Polygon ao processo.
Configurando a Polygon
Vamos precisar de alguns tokens MATIC em nossa carteira para fazer qualquer coisa. Portanto, o primeiro passo é garantir que você tenha uma carteira Ethereum. Basta acessar o site da Metamask, instalar a extensão e seguir o processo de criação de uma nova carteira. Obviamente, se você já possui uma carteira, pode pular esta etapa.
Depois de criar sua carteira, você precisará obter sua chave privada. Essa chave será necessária para implantar seu contrato NFT e interagir com a cadeia da camada 2. Na extensão Metamask, você pode exportar sua chave privada clicando no ícone do menu com três pontos, que fica ao lado do link para acessar sua conta:
Escolha “Detalhes da conta” e lá você poderá exportar sua chave privada. Quando você tiver a chave privada, fará duas coisas:
- Crie um arquivo chamado
.env
na raiz do seu projetopolygon-nft
e adicione isso a ele:PRIVATE_KEY=SUA_CHAVE_PRIVADA_EXPORTADA
. SubstituaSUA_CHAVE_PRIVADA_EXPORTADA
por sua própria chave privada. - No arquivo
.gitignore
do seu projeto, adicione uma linha que simplesmente diz.env
. Isso garantirá que você não faça um commit acidental do arquivo.env
para o controlador de versão e exponha sua chave.
Agora, como estamos usando um arquivo de variável de ambiente, precisaremos instalar outra dependência em nosso projeto para facilitar o trabalho com essas variáveis. Execute o seguinte comando na raiz do diretório do seu projeto:
npm i dotenv
Agora, vamos pegar alguns tokens MATIC da testnet. Vá até o faucet da testnet por aqui. Cole o endereço da sua carteira no campo fornecido, selecione o token MATIC e selecione a rede Mumbai.
Ao enviar a solicitação, será solicitada a sua confirmação. Faça isso e em breve você terá tokens MATIC disponíveis em sua carteira.
Ok, agora vamos atualizar o arquivo hardhat.config.js
para que ele saiba que estamos usando a rede Polygon. Na parte superior deste arquivo acima da primeira declaração require
, adicione:
require('dotenv').config();
const PRIVATE_KEY = process.env.PRIVATE_KEY;
Isso está dizendo a esse arquivo que usaremos o pacote dotenv
para extrair nossas variáveis de ambiente. A segunda linha está criando uma variável que faz referência à variável de ambiente que armazenamos em .env
para nossa chave privada.
Em seguida, você precisará encontrar o objeto module.exports
no arquivo. Provavelmente conterá apenas uma definição da versão Solidity. Vamos substituir todo esse código module.exports
por este:
module.exports = {
defaultNetwork: "matic",
networks: {
hardhat: {
},
matic: {
url: "https://rpc-mumbai.maticvigil.com",
accounts: [PRIVATE_KEY]
}
},
solidity: {
version: "0.8.0",
settings: {
optimizer: {
enabled: true,
runs: 200
}
}
},
paths: {
sources: "./contracts",
tests: "./test",
cache: "./cache",
artifacts: "./artifacts"
},
mocha: {
timeout: 20000
}
}
Parece que muita coisa está acontecendo aqui, mas não é tão ruim quanto parece. Estamos dizendo ao Hardhat que estamos usando a Polygon, onde encontrar o endereço RPC (a API que se comunica com a rede) e nossa chave privada. Fora isso, há apenas algumas configurações que não são especialmente importantes para você entender.
Antes de fazermos qualquer outra coisa, seria bom testar nossa configuração. Vamos implantar nosso contrato de exemplo Greeter.sol
na rede de teste Polygon. Execute o seguinte:
npx hardhat run scripts/sample-script.js --network matic
Isso está apontando o Hardhat para o script a ser executado e especificando a rede a ser usada. Definimos a rede matic
no hardhat.config.js
. Depois de alguns momentos, você deve ver algo assim no seu terminal:
Compiling 2 files with 0.7.3
Compilation finished successfully
Greeter deployed to: 0x790a1c9a212A13Fce5C1cfA4904f18bD3540E1e8
Seu endereço de contrato será diferente do meu, mas se você vir algo assim, significa que o arquivo de configuração está funcionando conforme o esperado.
Acabamos não executando nosso teste de exemplo porque não foi necessário. Na verdade, não precisamos testar o contrato inteligente, pois o substituiremos completamente. O que precisávamos testar era se nosso arquivo hardhat.config.js
estava funcionando conforme o esperado.
E está!
O Contrato
Vamos usar o OpenZeppelin para nos ajudar com o início do nosso contrato. Nosso contrato NFT será baseado no ERC-721. Então, precisamos instalar a biblioteca de contratos do OpenZeppelin. Do diretório do seu projeto no terminal, execute:
npm install @openzeppelin/contracts
Uma vez instalado, podemos começar a construir nosso contrato. Abra a pasta contracts
e crie um novo arquivo dentro dela chamado NFT.sol
(ou como você quiser chamá-lo, desde que tenha uma extensão .sol). Este contrato será muito simples, então vou mostrá-lo abaixo e te guiar através de cada linha:
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract MyNFT is ERC721, Ownable {
using Counters for Counters.Counter;
using Strings for uint256;
Counters.Counter private _tokenIds;
mapping (uint256 => string) private _tokenURIs;
constructor() ERC721("MyNFT", "MNFT") {}
function _setTokenURI(uint256 tokenId, string memory _tokenURI)
internal
virtual
{
_tokenURIs[tokenId] = _tokenURI;
}
function tokenURI(uint256 tokenId)
public
view
virtual
override
returns (string memory)
{
require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
string memory _tokenURI = _tokenURIs[tokenId];
return _tokenURI;
}
function mint(address recipient, string memory uri)
public
returns (uint256)
{
_tokenIds.increment();
uint256 newItemId = _tokenIds.current();
_mint(recipient, newItemId);
_setTokenURI(newItemId, uri);
return newItemId;
}
}
Como estamos aproveitando o contrato ERC721 do OpenZeppelin, há menos código para escrevermos. Vamos percorrer o código.
A primeira linha está definindo a versão do Solidity que estamos usando. Eu gosto de viver perigosamente, então estamos usando a versão mais recente (no momento em que escrevo isso).
As próximas três linhas estão trazendo os contratos do OpenZeppelin que precisamos, sendo o contrato ERC-721 o mais importante. Ter esses contratos já escritos realmente reduz o tempo necessário para criarmos contratos inteligentes.
Em seguida, estamos definindo a classe do nosso contrato e dando a ela o nome muito criativo de MyNFT
. Nossa definição de classe está estendendo as definições de classe existentes do contrato ERC721 e do contrato Ownable fornecido a nós pelo OpenZeppelin.
Depois disso, estamos definindo algumas variáveis. Counters e Strings são auxiliares para contagens e conversões de dados em strings. Depois disso, definimos uma variável para armazenar o próximo ID do token. Por fim, temos um mapeamento do ID do token e da URI do token.
Com todas as nossas variáveis definidas e importações incluídas, podemos chegar ao mais fundamental. Temos um construtor que está literalmente, e simplesmente, definindo o nome e o símbolo do token. Novamente, estou sendo muito criativo aqui com meu nome simbólico de MyNFT
e símbolo de MNFT
.
A partir da atualização mais recente do OpenZeppelin, seus contratos ERC-721 não têm mais uma função setTokenURI
integrada. A decisão parece ter sido projetada em torno da ideia de reduzir os custos de implantação de gas para usuários que não precisam dessa função. Mas nós precisamos dessa função.
No entanto, está tudo bem. Podemos novamente implementar esta função. É isso que nossa primeira função está fazendo. A função _setTokenURI
está recebendo um ID de token e uma URI de token e adicionando-os ao mapeamento.
Ao fazer isso, agora podemos permitir o retorno de uma URI de token com nossa função tokenURI
. Isso é importante porque a URI do token aponta para todos os metadados do NFT. Isso significa o nome, os atributos e o próprio ativo. Temos que garantir que a URI seja armazenada e possa ser devolvida a qualquer pessoa interessada.
Finalmente, temos nossa função mint
. Ela faz exatamente o que diz, ou seja, a cunhagem. Essa função recebe um endereço de carteira e uma URI de token. A URI do token é usada na função _setTokenURI
, o ID do token é incrementado. O novo token é enviado para o endereço do destinatário.
É isso. Esse é o contrato. Bem simples, certo? Agora, podemos testá-lo.
Testando
Executamos o primeiro teste do arquivo de configuração diretamente na rede de teste Polygon Mumbai. Não vamos fazer isso para o teste inicial do nosso contrato. Em vez disso, usaremos a rede na memória do Hardhat para simular a blockchain e a implantação.
Para fazer isso, precisamos retornar ao nosso arquivo hardhat.config.js
. Vamos alterar temporariamente o defaultNetwork
de “matic” para “hardhat”. Você também precisará comentar esta seção da configuração:
matic: {
url: "https://rpc-mumbai.maticvigil.com",
accounts: [PRIVATE_KEY]
}
Salve as alterações, pois estamos quase prontos para testar.
Precisamos fazer duas coisas. Primeiro, vá em frente e encontre a pasta artifacts
na raiz do diretório do seu projeto. Dentro dela, exclua a pasta contracts
. Em seguida, localize a pasta contracts
na raiz do seu projeto e exclua o contrato Greeter.sol
.
Você é deixado apenas com seu contrato NFT.sol
. Precisamos também atualizar nosso arquivo de teste. Então, dentro da sua pasta test
, abra o arquivo sample-test.js
. Substitua tudo que está dentro dentro por:
const { expect } = require("chai");
describe("NFT", function() {
it("O contrato deve ser implantado, o token cunhado, e a resolução feita para a URI correta", async function() {
const NFT = await ethers.getContractFactory("MyNFT");
const nft = await NFT.deploy();
const URI = "ipfs://QmWJBNeQAm9Rh4YaW8GFRnSgwa4dN889VKm9poc2DQPBkv";
await nft.deployed();
await nft.mint("0x7028f6756a9b815711bc2d37e8d5be23fdac846d", URI)
expect(await nft.tokenURI(1)).to.equal(URI)
});
});
Neste teste, estamos recebendo (lembre-se que chamamos o contrato de MyNFT
) e implantando nosso contrato. Então estamos cunhando um token com um endereço Ethereum falso e uma URI de token. Observe o formato da URI. Queremos fornecer nossos metadados via IPFS e Pinata, mas queremos que esses metadados sejam resolvidos a partir de qualquer gateway IPFS, não apenas do Pinata. Formatar a URI dessa maneira ajuda a comprovar a resolução desses metadados no futuro.
A URI usada no exemplo acima é falsa, mas em breve estaremos criando uma verdadeira e cunhando um NFT na rede Polygon. Mas primeiro, vamos garantir que nosso contrato seja válido. Para testá-lo, em seu terminal, execute o seguinte comando:
npx hardhat test
Você deve ver um resultado assim:
Podemos escrever vários outros testes como este, e deveríamos. Mas vou deixar isso para você. O formato do teste que escrevemos juntos deve ajudá-lo a escrever testes adicionais.
Acho que estamos prontos para cunhar um token na rede de testes Polygon Mumbai.
Cunhando
Antes de podermos cunhar um NFT, precisamos criar um ativo e fixá-lo no IPFS. Eu tenho uma imagem que vou usar, mas você pode usar qualquer arquivo que quiser. Aqui está a imagem que vou usar:
Precisaremos colocar isso na rede IPFS. Usaremos Pinata para isso. Inscreva-se ou faça login aqui. Depois de se inscrever, vá até o Pin Manager e faça o upload do seu arquivo. Ao fazer o upload, você verá o arquivo na tabela do Pin Manager:
Você precisará clicar no botão “copiar” ao lado do CID IPFS que você obteve para o seu arquivo. Depois de copiá-lo, cole-o em algum lugar seguro para usar mais tarde.
Fizemos o upload do ativo, mas precisamos fazer o upload de um arquivo JSON, que representa os metadados de nosso NFT. Isso pode parecer difícil, mas na verdade é bem simples. Escrevemos sobre esses metadados aqui, mas vou mostrar como fazer isso neste post.
Em um editor de texto (pode ser o bloco de notas ou qualquer outro editor de texto simples em seu computador), crie um novo arquivo. Nesse arquivo, adicione o seguinte:
{
"name": "MyNFT",
"description": "Este é o meu NFT",
"image": "ipfs://QmQRDYPTprDPknn3zE2ZSpc8DjnUKfYztyaeMBYdR8GDdw"
}
Você pode nomear seu NFT como quiser e fornecer a descrição que desejar. A propriedade image
, no entanto, precisa apontar para o CID que você recebeu do ativo que foi enviado no upload anterior. Se você precisar obter o CID novamente, poderá copiá-lo do Pin Manager. Queremos que o ativo seja carregado de qualquer gateway IPFS, portanto, para deixar as coisas à prova de futuro, vamos formatar a URI do nosso ativo desta forma: ipfs://SEU_CID
.
Depois de fazer isso, salve esse arquivo como metadata.json
. Depois de salvar, você pode voltar para o Pin Manager do Pinata e fazer o upload desse arquivo. Novamente, ao fazer isso, você receberá um CID IPFS. Copie o CID porque é o que usaremos quando cunharmos nosso NFT.
Há todo um tutorial adicional de informações sobre como implantar seu contrato inteligente e permitir que as pessoas criem NFTs de dentro de um aplicativo da web que você cria. Talvez eu escreva sobre isso algum dia, mas hoje, vamos implantar e cunhar a partir da linha de comando. Vamos modificar o arquivo sample-script.js
de dentro de nossa pasta scripts
para fazer isso. Na verdade, vamos alterar o nome desse arquivo para deploy-script.js
. Dentro do arquivo, substitua tudo por isso:
const hre = require("hardhat");
async function main() {
const NFT = await hre.ethers.getContractFactory("MyNFT");
const nft = await NFT.deploy();
await nft.deployed();
console.log("NFT implantado em:", nft.address);
}
main().then(() => process.exit(0)).catch(error => {
console.error(error);
process.exit(1);
});
Para implantar isso na rede de testes Mumbai, precisamos atualizar nosso arquivo hardhat.config.js
mais uma vez. Altere o defaultNetwork
de volta para “matic”. Agora, descomente a seção “matic” que comentamos anteriormente.
No seu terminal, execute o seguinte comando:
npx hardhat run scripts/deploy-script.js --network matic
Se tudo correr bem agora, seu contrato NFT será cunhado. O output em seu terminal incluirá o endereço do contrato. Copie este endereço porque vamos precisar dele para cunhar nosso primeiro NFT.
Para cunhar um token, vamos criar um novo script. Dentro da pasta scripts
, crie um arquivo chamado mint-script.js
. Dentro desse arquivo, adicione o seguinte:
const hre = require("hardhat");
async function main() {
const NFT = await hre.ethers.getContractFactory("MyNFT");
const URI = "ipfs://SEU_CID_DE_METADADOS"
const WALLET_ADDRESS = "ENDEREÇO_DA_SUA_CARTEIRA"
const CONTRACT_ADDRESS = "ENDEREÇO DO SEU CONTRATO NFT"
const contract = NFT.attach(CONTRACT_ADDRESS);
await contract.mint(WALLET_ADDRESS, URI);
console.log("NFT cunhado:", contract);
}
main().then(() => process.exit(0)).catch(error => {
console.error(error);
process.exit(1);
});
Este é um script bem simples. Ele contém a URI dos metadados que enviamos para o Pinata, o seu endereço da carteira Ethereum e o endereço do contrato que você obteve quando implantou o contrato NFT.
Salve esse arquivo e você está pronto para cunhar! Execute o seguinte comando no seu terminal:
npx hardhat run scripts/mint-script.js --network matic
Supondo que não haja erros, você acabou de criar um NFT para o ativo que criou anteriormente. Está na sua carteira. Para provar isso, podemos executar mais um script. Crie um novo arquivo na pasta scripts
chamado get-token-script.js
. Dentro desse arquivo adicione o seguinte:
const hre = require("hardhat");
async function main() {
const NFT = await hre.ethers.getContractFactory("MyNFT");
const CONTRACT_ADDRESS = "ENDEREÇO_DO_SEU_CONTRATO"
const contract = NFT.attach(CONTRACT_ADDRESS);
const owner = await contract.ownerOf(1);
console.log("Proprietário:", owner);
const uri = await contract.tokenURI(1);
console.log("URI: ", uri);
}
main().then(() => process.exit(0)).catch(error => {
console.error(error);
process.exit(1);
});
Este script está perguntando quem é o proprietário do primeiro token criado com nosso contrato NFT. Sabemos que criamos apenas um token. Ele também busca a URI do token para esse ID do token.
Você pode executar este script com o seguinte comando:
npx hardhat run scripts/get-token-script.js --network matic
Você verá algo assim no terminal:
Owner: 0xd7220ab26a887a60Af3a11178eF4A48BE8DncbA6URI: ipfs://QmZu6UUMHo2bHLiRMZCoQf7hiSmnFVVzWqnEAyF9SJwxhx
Seu endereço de proprietário e URI serão diferentes, mas você entendeu.
Você também notará o quão rápido executamos estas transações. Criar o contrato, cunhar um token, buscar informações sobre o token – foi tudo muito rápido. Esse é o poder da Polygon. Temos também o fato de que o processo não nos custou muito gas. Se você verificar o saldo da sua carteira aqui, verá que não gastou muito desde que recebeu seus tokens do faucet.
Finalizando
O próximo passo seria fazer a ponte do seu NFT entre a rede Polygon e a cadeia principal da Ethereum. Felizmente, a Polygon facilita bem essa questão. Este tutorial ficou bem longo, então não abordaremos isso aqui. Confira mais informações sobre esse assunto nesta excelente documentação da Polygon.
O mais incrível de tudo que acabamos de fazer é que colocamos o arquivo do nosso ativo no IPFS. Não importa em qual blockchain o NFT viva, podemos ter certeza que o ativo sempre apontará para o CID que foi gerado quando carregamos o arquivo para o IPFS por meio do Pinata.
Se você quiser revisar o código-fonte deste tutorial, você pode encontrá-lo aqui.
Que você tenha muitos pins bem-sucedidos!
Este artigo foi escrito por Justin Hunter, e traduzido por Paulinho Giovannini. Encontre o artigo original aqui.
Oldest comments (0)