Tutorial de criação de uma NFT
Um dos maiores desafios para desenvolvedores vindos de um background Web2 é descobrir como conectar seu contrato inteligente a um projeto frontend e interagir com ele.
Ao criar um minter NFT — uma simples UI onde você pode inserir um link para seu ativo digital, um título e uma descrição — você aprenderá a:
- Conectar ao MetaMask através do seu projeto frontend
- Chamar métodos de contrato inteligentes no seu frontend
- Assine transações usando MetaMask
Neste tutorial, usaremos o React (opens in a new tab) como nossa estrutura de frontend. Como este tutorial está focado principalmente no desenvolvimento da Web3, nós não passaremos muito tempo detalhando os fundamentos do React. Em vez disso, nós focaremos em trazer funcionalidade para o nosso projeto.
Como pré-requisito, você deve ter uma compreensão mínima do React – saber como funcionam componentes, props, useState/useEffect e chamadas de funções básicas. Se você nunca ouviu falar de nenhum desses termos antes, talvez queira conferir este tutorial de Introdução ao React (opens in a new tab). Para os alunos mais visuais, recomendamos fortemente esta excelente série de vídeos Full Modern React Tutorial (opens in a new tab) do Net Ninja.
E se você ainda não fez, você definitivamente precisará criar uma conta Alchemy para concluir este tutorial, bem como construir qualquer coisa no blockchain. Cadastre-se para uma conta gratuita aqui (opens in a new tab).
Sem mais delongas, vamos começar!
Criando NFTs 101
Antes de começarmos a olhar para qualquer código, é importante entender como funciona fazer uma NFT. Envolve duas etapas:
Publique um contrato inteligente de NFT na blockchain da Ethereum
A maior diferença entre os dois padrões de contrato inteligente NFT é que o ERC-1155 é um padrão multi-token e inclui a funcionalidade de lote, enquanto o ERC-721 é um padrão de token único, portanto, suporta apenas a transferência de um token por vez.
Chame a função de mintagem
Normalmente, esta função de mintagem exige que você passe duas variáveis como parâmetros: primeiro o recipient, que especifica o endereço que receberá seu NFT recém-mintado, e segundo o tokenURI do NFT, uma string que resolve para um documento JSON descrevendo os metadados do NFT.
Os metadados de uma NFT são o que realmente a torna realidade, permitindo que tenha propriedades configuráveis, como um nome, descrição, imagem (ou diferentes ativos digitais), e outros atributos. Aqui está um exemplo de um tokenURI (opens in a new tab), que contém os metadados de um NFT.
Neste tutorial, vamos nos concentrar na parte 2, chamando a função mint de contrato inteligente de uma NFT existente usando nossa interface do React.
Aqui está um link (opens in a new tab) para o contrato inteligente de NFT ERC-721 que chamaremos neste tutorial. Se você quiser saber como o fizemos, recomendamos que confira nosso outro tutorial, "Como Criar um NFT" (opens in a new tab).
Legal, agora que entendemos como fazer uma NFT funcionar, vamos clonar nossos arquivos iniciais!
Clone os arquivos iniciais
Primeiro, acesse o repositório do GitHub nft-minter-tutorial (opens in a new tab) para obter os arquivos iniciais deste projeto. Clone este repositório para o seu ambiente local.
Ao abrir este repositório nft-minter-tutorial clonado, você notará que ele contém duas pastas: minter-starter-files e nft-minter.
minter-starter-filescontém os arquivos iniciais (essencialmente a UI do React) para este projeto. Neste tutorial, estaremos trabalhando neste diretório, à medida que você aprende como dar vida a esta UI, conectando-a à sua carteira Ethereum e a um contrato inteligente de NFT.nft-mintercontém o tutorial completo e está lá para você como uma referência caso você trave.
Em seguida, abra sua cópia de minter-starter-files em seu editor de código e navegue até a pasta src.
Todo o código que escrevermos ficará na pasta src. Estaremos editando o componente Minter.js e escrevendo arquivos javascript adicionais para dar ao nosso projeto a funcionalidade Web3.
Passo 2: Confira nossos arquivos iniciais
Antes de começarmos a codificar, é importante verificar o que já está fornecido para nós nos arquivos iniciais.
Coloque seu projeto react para funcionar
Vamos começar executando o projeto React em nosso navegador. A beleza do React é que uma vez que nosso projeto esteja sendo executado no nosso navegador, qualquer alteração que salvarmos será atualizada ao vivo em nosso navegador.
Para colocar o projeto para funcionar, navegue até o diretório raiz da pasta minter-starter-files e execute npm install em seu terminal para instalar as dependências do projeto:
cd minter-starter-filesnpm installAssim que a instalação for concluída, execute npm start em seu terminal:
npm startFeito isso, você deve abrir http://localhost:3000/ (opens in a new tab) no seu navegador, onde você verá o frontend do nosso projeto. Ele deve consistir de 3 campos: um local para inserir um link para o ativo do seu NFT, digite o nome da sua NFT e forneça uma descrição.
Se você tentar clicar nos botões "Connectar Wallet" ou "Mint NFT", você notará que eles não funcionam — isso porque ainda precisamos programar a funcionalidade deles! :)
O componente Minter.js
OBSERVAÇÃO: Certifique-se de que você está na pasta minter-starter-files e não na pasta nft-minter!
Vamos voltar para a pasta src em nosso editor e abrir o arquivo Minter.js. É muito importante que entendamos tudo neste arquivo, pois é o principal componente do React no qual vamos trabalhar.
No topo do nosso arquivo, temos nossas variáveis de estado que serão atualizadas após eventos específicos.
1//Variáveis de estado2const [walletAddress, setWallet] = useState("")3const [status, setStatus] = useState("")4const [name, setName] = useState("")5const [description, setDescription] = useState("")6const [url, setURL] = useState("")Nunca ouviu falar de variáveis de estado do React ou State Hooks? Confira esta (opens in a new tab) documentação.
Veja aqui o que cada uma das variáveis representa:
walletAddress- uma string que armazena o endereço da carteira do usuáriostatus- uma string que contém uma mensagem para exibir na parte inferior da UIname- uma string que armazena o nome do NFTdescription- uma string que armazena a descrição do NFTurl- uma string que é um link para o ativo digital do NFT
Após as variáveis de estado, você verá três funções não implementadas: useEffect, connectWalletPressed e onMintPressed. Você notará que todas essas funções são async, porque faremos chamadas de API assíncronas nelas! Os nomes delas são relacionadas com sua funcionalidade:
1useEffect(async () => {2 //TODO: implementar3}, [])45const connectWalletPressed = async () => {6 //TODO: implementar7}89const onMintPressed = async () => {10 //TODO: implementar11}Exibir tudouseEffect(opens in a new tab) - este é um hook do React que é chamado depois que seu componente é renderizado. Como ele tem umapropde array vazio[]passada para ele (veja a linha 3), ele só será chamado na primeira renderização do componente. Aqui vamos chamar nosso ouvinte de carteira e outra função de carteira para atualizar nossa interface de usuário para refletir se uma carteira já está conectada.connectWalletPressed- esta função será chamada para conectar a carteira MetaMask do usuário ao nosso dapp.onMintPressed- esta função será chamada para mintar o NFT do usuário.
Perto do final desse arquivo, temos a interface de usuário do nosso componente. Se você analisar este código com atenção, notará que atualizamos nossas variáveis de estado url, name e description quando a entrada em seus campos de texto correspondentes muda.
Você também verá que connectWalletPressed e onMintPressed são chamadas quando os botões com os IDs mintButton e walletButton são clicados, respectivamente.
1//a UI do nosso componente2return (3 <div className="Minter">4 <button id="walletButton" onClick={connectWalletPressed}>5 {walletAddress.length > 0 ? (6 "Conectado: " +7 String(walletAddress).substring(0, 6) +8 "..." +9 String(walletAddress).substring(38)10 ) : (11 <span>Conectar Carteira</span>12 )}13 </button>1415 <br></br>16 <h1 id="title">🧙♂️ Mintador de NFT da Alchemy</h1>17 <p>18 Basta adicionar o link, nome e descrição do seu ativo e, em seguida, pressione "Mintar".19 </p>20 <form>21 <h2>🖼 Link para o ativo: </h2>22 <input23 type="text"24 placeholder="ex: https://gateway.pinata.cloud/ipfs/<hash>"25 onChange={(event) => setURL(event.target.value)}26 />27 <h2>🤔 Nome: </h2>28 <input29 type="text"30 placeholder="ex: Meu primeiro NFT!"31 onChange={(event) => setName(event.target.value)}32 />33 <h2>✍️ Descrição: </h2>34 <input35 type="text"36 placeholder="ex: Ainda mais legal que os cryptokitties ;)"37 onChange={(event) => setDescription(event.target.value)}38 />39 </form>40 <button id="mintButton" onClick={onMintPressed}>41 Mintar NFT42 </button>43 <p id="status">{status}</p>44</div>45)Exibir tudoFinalmente, vamos endereçar onde esse componente Minter será adicionado.
Se você for ao arquivo App.js, que é o componente principal no React que atua como um contêiner para todos os outros componentes, você verá que nosso componente Minter é injetado na linha 7.
Neste tutorial, editaremos apenas o arquivo Minter.js e adicionaremos arquivos em nossa pasta src.
Agora que entendemos com o que estamos trabalhando, vamos configurar a nossa carteira Ethereum!
Configure sua carteira Ethereum
Para que os usuários possam interagir com o seu contrato inteligente, eles precisarão conectar a sua carteira Ethereum ao seu dapp.
Baixe o MetaMask
Para este tutorial, usaremos uma carteira virtual no navegador, a MetaMask, para gerenciar o endereço da sua conta Ethereum. Se você quiser entender mais sobre como as transações na Ethereum funcionam, confira esta página.
Você pode baixar e criar uma conta MetaMask gratuitamente aqui (opens in a new tab). Quando estiver criando uma conta, ou se já tiver uma, certifique-se de mudar para a "Ropsten Test Network", no canto superior direito (para não precisar lidar com dinheiro de verdade).
Adicione ether de um Faucet
Para mintar as nossas NFT (ou assinar quaisquer transações no blockchain Ethereum), precisaremos de alguns Eth falsos. Para obter Eth, você pode ir ao Ropsten faucet (opens in a new tab) e inserir o endereço da sua conta Ropsten, depois clicar em “Send Ropsten Eth.” Em seguida, você deve ver Eth em sua conta MetaMask!
Verifique seu saldo
Para verificar novamente se nosso saldo está lá, vamos fazer uma solicitação eth_getBalance (opens in a new tab) usando a ferramenta de composição da Alchemy (opens in a new tab). Ela mostrará a quantidade de Eth na sua carteira. Depois de inserir o endereço da sua conta da MetaMask e clicar em "Send Request", você verá uma resposta como esta:
1{"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"}OBSERVAÇÃO: Este resultado está em wei, não em eth. Lembre-se de que "Wei" é a menor unidade de ether. A conversão de wei para eth é: 1 eth = 10¹⁸ wei. Então, se convertemos 0xde0b6b3a7640000 para decimal, temos 1*10¹⁸ wei, que é igual a 1 eth.
Ufa! Nosso dinheiro falso está todo lá!
Conecte o MetaMask à sua UI
Agora que nossa carteira MetaMask está configurada, vamos conectar nosso dapp a ela!
Como queremos aderir ao paradigma MVC (opens in a new tab), vamos criar um arquivo separado que contém nossas funções para gerenciar a lógica, os dados e as regras do nosso dapp e, em seguida, passar essas funções para o nosso frontend (nosso componente Minter.js).
A função connectWallet
Para fazer isso, vamos criar uma nova pasta chamada utils em seu diretório src e adicionar um arquivo chamado interact.js dentro dela, que conterá todas as nossas funções de interação com a carteira e o contrato inteligente.
Em nosso arquivo interact.js, escreveremos uma função connectWallet, que então importaremos e chamaremos em nosso componente Minter.js.
No seu arquivo interact.js, adicione o seguinte
1export const connectWallet = async () => {2 if (window.ethereum) {3 try {4 const addressArray = await window.ethereum.request({5 method: "eth_requestAccounts",6 })7 const obj = {8 status: "👆🏽 Escreva uma mensagem no campo de texto acima.",9 address: addressArray[0],10 }11 return obj12 } catch (err) {13 return {14 address: "",15 status: "😥 " + err.message,16 }17 }18 } else {19 return {20 address: "",21 status: (22 <span>23 <p>24 {" "}25 🦊 <a target="_blank" href={`https://metamask.io/download`}>26 Você deve instalar o MetaMask, uma carteira virtual Ethereum, em seu navegador.27 </a>28 </p>29 </span>30 ),31 }32 }33}Exibir tudoVamos dividir o que este código faz:
Primeiro, nossa função verifica se o window.ethereum está ativado em seu navegador.
window.ethereum é uma API global injetada pelo MetaMask e outros provedores de carteira que permite que sites solicitem as contas Ethereum dos usuários. Se aprovada, ela pode ler dados das blockchains ao qual o usuário está conectado e sugerir que o usuário assine mensagens e transações. Confira a documentação do MetaMask (opens in a new tab) para mais informações!
Se o window.ethereum não estiver presente, isso significa que o MetaMask não está instalado. Isso resulta no retorno de um objeto JSON, onde o address retornado é uma string vazia, e o objeto JSX status informa que o usuário deve instalar o MetaMask.
A maioria das funções que escrevermos retornará objetos JSON que podemos usar para atualizar nossas variáveis de estado e UI.
Agora, se o window.ethereum estiver presente, é aí que as coisas ficam interessantes.
Usando um loop try/catch, tentaremos nos conectar ao MetaMask chamando window.ethereum.request({ method: \"eth_requestAccounts\" }); (opens in a new tab). Chamando esta função o MetaMask irá abrir no navegador, onde o usuário será solicitado a conectar sua carteira ao seu dapp.
- Se o usuário optar por se conectar, o
method: \"eth_requestAccounts\"retornará um array que contém todos os endereços de conta do usuário que estão conectados ao dapp. No total, nossa funçãoconnectWalletretornará um objeto JSON que contém o primeiroaddressneste array (veja a linha 9) e uma mensagem destatusque solicita ao usuário que escreva uma mensagem para o contrato inteligente. - Se o usuário rejeitar a conexão, o objeto JSON conterá uma string vazia para o
addressretornado e uma mensagem destatusque reflete que o usuário rejeitou a conexão.
Adicione a função connectWallet ao seu Componente de UI Minter.js
Agora que escrevemos esta função connectWallet, vamos conectá-la ao nosso componente Minter.js.
Primeiro, teremos que importar nossa função para o nosso arquivo Minter.js, adicionando import { connectWallet } from "./utils/interact.js"; no topo do arquivo Minter.js. Suas primeiras 11 linhas de Minter.js agora devem se parecer com isto:
1import { useEffect, useState } from "react";2import { connectWallet } from "./utils/interact.js";34const Minter = (props) => {56 //Variáveis de estado7 const [walletAddress, setWallet] = useState("");8 const [status, setStatus] = useState("");9 const [name, setName] = useState("");10 const [description, setDescription] = useState("");11 const [url, setURL] = useState("");Exibir tudoEntão, dentro da nossa função connectWalletPressed, vamos chamar nossa função importada connectWallet, assim:
1const connectWalletPressed = async () => {2 const walletResponse = await connectWallet()3 setStatus(walletResponse.status)4 setWallet(walletResponse.address)5}Observe como a maior parte da nossa funcionalidade é abstraída do nosso componente Minter.js a partir do arquivo interact.js? É assim que respeitamos o paradigma M-V-C!
Em connectWalletPressed, nós simplesmente fazemos uma chamada await para a nossa função connectWallet importada e, usando sua resposta, atualizamos nossas variáveis status e walletAddress através de seus hooks de estado.
Agora, vamos salvar os dois arquivos Minter.js e interact.js e testar nossa UI até agora.
Abra seu navegador em localhost:3000, e pressione o botão "Conectar Carteira" no canto superior direito da página.
Se você tiver o MetaMask instalado, você será solicitado a conectar sua carteira ao seu dapp. Aceite o convite para se conectar.
Você verá que o botão da carteira agora reflete que seu endereço está conectado.
Em seguida, tente atualizar a página... que estranho. Nosso botão de carteira está nos pedindo para conectar o MetaMask, mesmo que já esteja conectado...
Mas não se preocupe! Podemos corrigir isso facilmente implementando uma função chamada getCurrentWalletConnected, que verificará se um endereço já está conectado ao nosso dapp e atualizará nossa UI de acordo!
A função getCurrentWalletConnected
No seu arquivo interact.js, adicione a seguinte função getCurrentWalletConnected:
1export const getCurrentWalletConnected = async () => {2 if (window.ethereum) {3 try {4 const addressArray = await window.ethereum.request({5 method: "eth_accounts",6 })7 if (addressArray.length > 0) {8 return {9 address: addressArray[0],10 status: "👆🏽 Escreva uma mensagem no campo de texto acima.",11 }12 } else {13 return {14 address: "",15 status: "🦊 Conecte-se ao MetaMask usando o botão no canto superior direito.",16 }17 }18 } catch (err) {19 return {20 address: "",21 status: "😥 " + err.message,22 }23 }24 } else {25 return {26 address: "",27 status: (28 <span>29 <p>30 {" "}31 🦊 <a target="_blank" href={`https://metamask.io/download`}>32 Você deve instalar o MetaMask, uma carteira virtual Ethereum, em seu navegador.33 </a>34 </p>35 </span>36 ),37 }38 }39}Exibir tudoEste código é muito semelhante à função connectWallet que escrevemos anteriormente.
A principal diferença é que, em vez de chamar o método eth_requestAccounts, que abre o MetaMask para o usuário conectar sua carteira, aqui chamamos o método eth_accounts, que simplesmente retorna uma matriz contendo os endereços do MetaMask atualmente conectados ao nosso dapp.
Para ver essa função em ação, vamos chamá-la na função useEffect do nosso componente Minter.js.
Como fizemos para connectWallet, devemos importar essa função do nosso arquivo interact.js para o nosso arquivo Minter.js da seguinte forma:
1import { useEffect, useState } from "react"2import {3 connectWallet,4 getCurrentWalletConnected, //importar aqui5} from "./utils/interact.js"Agora, nós simplesmente a chamamos em nossa função useEffect:
1useEffect(async () => {2 const { address, status } = await getCurrentWalletConnected()3 setWallet(address)4 setStatus(status)5}, [])Observe que usamos a resposta da nossa chamada para getCurrentWalletConnected para atualizar nossas variáveis de estado walletAddress e status.
Depois de adicionar este código, tente atualizar a janela do navegador. O botão deve dizer que você está conectado e mostrar uma visualização do endereço de sua carteira conectada - mesmo depois de atualizar!
Implemente addWalletListener
O passo final na configuração da nossa carteira dapp é implementar o ouvinte de carteira, para que nossa interface atualize quando o estado mudar, como quando o usuário desconecta ou troca de contas.
No seu arquivo Minter.js, adicione uma função addWalletListener que se pareça com o seguinte:
1function addWalletListener() {2 if (window.ethereum) {3 window.ethereum.on("accountsChanged", (accounts) => {4 if (accounts.length > 0) {5 setWallet(accounts[0])6 setStatus("👆🏽 Escreva uma mensagem no campo de texto acima.")7 } else {8 setWallet("")9 setStatus("🦊 Conecte-se ao MetaMask usando o botão superior direito.")10 }11 })12 } else {13 setStatus(14 <p>15 {" "}16 🦊 <a target="_blank" href={`https://metamask.io/download`}>17 Você deve instalar o MetaMask, uma carteira virtual Ethereum, em seu navegador.18 </a>19 </p>20 )21 }22}Exibir tudoVamos dividir rapidamente o que está acontecendo aqui:
- Primeiro, nossa função verifica se o
window.ethereumestá ativado (ou seja, se o MetaMask está instalado).- Se não estiver, simplesmente definimos nossa variável de estado
statuscomo uma string JSX que solicita ao usuário que instale o MetaMask. - Se estiver habilitado, configuramos o listener
window.ethereum.on("accountsChanged")na linha 3 que escuta por mudanças de estado na carteira MetaMask, que incluem quando o usuário conecta uma conta adicional ao dapp, troca de contas ou desconecta uma conta. Se houver pelo menos uma conta conectada, a variável de estadowalletAddressé atualizada como a primeira conta no arrayaccountsretornado pelo listener. Caso contrário, owalletAddressé definido como uma string vazia.
- Se não estiver, simplesmente definimos nossa variável de estado
Finalmente, nós devemos chamá-la em nossa função useEffect:
1useEffect(async () => {2 const { address, status } = await getCurrentWalletConnected()3 setWallet(address)4 setStatus(status)56 addWalletListener()7}, [])E Voila! Concluímos a programação de toda a funcionalidade da nossa carteira! Agora que a nossa carteira está pronta, vamos descobrir como mintar nossa NFT!
Metadados de NFT 101
Lembra dos metadados da NFT que acabamos de falar no Passo 0 deste tutorial - ele dá vida a uma NFT, permitindo que tenha propriedades, como um ativo digital, nome, descrição e outros atributos.
Vamos precisar configurar esses metadados como um objeto JSON e armazená-lo, para que possamos passá-lo como o parâmetro tokenURI ao chamar a função mintNFT do nosso contrato inteligente.
No campo texto "Link to Asset", "Name", "Description" inclui as diferentes propriedades dos metadados de nosso NFT. Nós vamos formatar estes metadados como um objeto JSON, mas há algumas opções para onde podemos armazenar este objeto JSON:
- Poderíamos armazená-lo no blockchain Ethereum; no entanto, fazê-lo seria muito caro.
- Nós poderíamos armazená-lo em um servidor centralizado, como AWS ou Firebase. Mas isso iria contra nossa ética de descentralização.
- Poderíamos usar o IPFS, um protocolo descentralizado e uma rede peer-to-peer para armazenar e compartilhar dados em um sistema de arquivos distribuído. Como este protocolo é descentralizado e gratuito, essa é a melhor opção!
Para armazenar nossos metadados no IPFS, usaremos o Pinata (opens in a new tab), uma API e um kit de ferramentas IPFS convenientes. Na próxima etapa, vamos explicar exatamente como fazer isso!
Use o Pinata para fixar seus metadados no IPFS
Se você não tiver uma conta no Pinata (opens in a new tab), cadastre-se para obter uma conta gratuita aqui (opens in a new tab) e conclua as etapas para verificar seu e-mail e conta.
Crie sua chave de API do Pinata
Navegue até a página https://pinata.cloud/keys (opens in a new tab), selecione o botão "Nova Chave" na parte superior, defina o widget Admin como ativado e nomeie sua chave.
Será mostrado a você um pop-up com as informações da sua API. Certifique-se de colocar isto num lugar seguro.
Agora que a nossa chave está configurada, vamos adicioná-la ao nosso projeto para que possamos usá-la.
Crie um arquivo .env
Podemos armazenar com segurança nossa chave e segredo do Pinata em um arquivo de ambiente. Vamos instalar o pacote dotenv (opens in a new tab) no diretório do seu projeto.
Abra uma nova aba em seu terminal (separada da que está executando o host local) e certifique-se de estar na pasta minter-starter-files. Em seguida, execute o seguinte comando em seu terminal:
1npm install dotenv --saveEm seguida, crie um arquivo .env no diretório raiz de seus minter-starter-files inserindo o seguinte na sua linha de comando:
1vim.envIsso abrirá seu arquivo .env no vim (um editor de texto). Para salvar, aperte "esc" + ":" + "q" no seu teclado nesta ordem.
Em seguida, no VSCode, navegue até o seu arquivo .env e adicione sua chave de API e segredo de API da Pinata a ele, da seguinte forma:
1REACT_APP_PINATA_KEY = <pinata-api-key>2REACT_APP_PINATA_SECRET = <pinata-api-secret>Salve o arquivo e então você estará pronto para começar a escrever a função de enviar seus metadados JSON para IPFS!
Implemente pinJSONToIPFS
Felizmente para nós, a Pinata tem uma API especificamente para carregar dados JSON para o IPFS (opens in a new tab) e um exemplo conveniente em JavaScript com axios que podemos usar, com algumas pequenas modificações.
Na sua pasta utils, vamos criar outro arquivo chamado pinata.js e então importar nosso segredo e chave do Pinata do arquivo .env da seguinte forma:
1require("dotenv").config()2const key = process.env.REACT_APP_PINATA_KEY3const secret = process.env.REACT_APP_PINATA_SECRETEm seguida, cole o código adicional abaixo no seu arquivo pinata.js. Não se preocupe, nós iremos clarificar o que tudo isso significa!
1require("dotenv").config()2const key = process.env.REACT_APP_PINATA_KEY3const secret = process.env.REACT_APP_PINATA_SECRET45const axios = require("axios")67export const pinJSONToIPFS = async (JSONBody) => {8 const url = `https://api.pinata.cloud/pinning/pinJSONToIPFS`9 //fazendo a requisição axios POST para o Pinata ⬇️10 return axios11 .post(url, JSONBody, {12 headers: {13 pinata_api_key: key,14 pinata_secret_api_key: secret,15 },16 })17 .then(function (response) {18 return {19 success: true,20 pinataUrl:21 "https://gateway.pinata.cloud/ipfs/" + response.data.IpfsHash,22 }23 })24 .catch(function (error) {25 console.log(error)26 return {27 success: false,28 message: error.message,29 }30 })31}Exibir tudoEntão, o que esse código faz exatamente?
Primeiro, ele importa o axios (opens in a new tab), um cliente HTTP baseado em promessas para o navegador e node.js, que usaremos para fazer uma requisição para o Pinata.
Em seguida, temos nossa função assíncrona pinJSONToIPFS, que recebe um JSONBody como entrada e a chave e segredo da API da Pinata em seu cabeçalho, tudo para fazer uma requisição POST para sua API pinJSONToIPFS.
- Se esta requisição POST for bem-sucedida, nossa função retornará um objeto JSON com o booleano
successcomo verdadeiro e opinataUrlonde nossos metadados foram fixados. Usaremos estepinataUrlretornado como a entradatokenURIpara a função de mintagem do nosso contrato inteligente. - Se esta solicitação de post falhar, nossa função retornará um objeto JSON com o booleano de
successcomo falso e uma stringmessageque transmite nosso erro.
Assim como nos tipos de retorno da nossa função connectWallet, estamos retornando objetos JSON para que possamos usar seus parâmetros para atualizar nossas variáveis de estado e a UI.
Carregue seu contrato inteligente
Agora que temos uma maneira de enviar nossos metadados de NFT para o IPFS através de nossa função pinJSONToIPFS, vamos precisar de uma forma de carregar uma instância do nosso contrato inteligente para que possamos chamar sua função mintNFT.
Como mencionamos anteriormente, neste tutorial usaremos este contrato inteligente de NFT existente (opens in a new tab); no entanto, se você quiser aprender como o fizemos ou criar um você mesmo, recomendamos fortemente que você confira nosso outro tutorial, "Como Criar um NFT." (opens in a new tab).
A ABI do contrato
Se você examinou nossos arquivos de perto, deve ter notado que em nosso diretório src, há um arquivo contract-abi.json. Um ABI é necessário para especificar qual função um contrato irá invocar, como também garantir que a função retornará dados no formato que você espera.
Também precisaremos de uma chave API Alchemy e da API Alchemy Web3 para conectar ao blockchain Ethereum e carregar o nosso contrato inteligente.
Crie sua chave de API da Alchemy
Se você ainda não tem uma conta na Alchemy, inscreva-se gratuitamente aqui. (opens in a new tab)
Assim que criar uma conta na Alchemy, você pode gerar uma chave de API criando um "app". Isso nos permitirá fazer solicitações à rede de testes Ropsten.
Navegue até a pagina "Create App" no seu "Dashboard da Alchemy", passe o cursor sob "Apps" na barra de navegação e clique em “Create App”.
Nomeie seu aplicativo; nós escolhemos "Minha primeira NFT!", faça uma breve descrição, selecione "Staging" para o ambiente (usado para a contabilidade do seu ‘app’) e escolha "Ropsten" para sua rede.
Clique em "Create App", e é isso e tudo! Seu app deveria aparecer na tabela abaixo.
Incrível agora que criamos a nossa URL de API Alchemy HTTP, copie-a para a sua área de transferência...
…e então vamos adicioná-lo ao nosso arquivo .env. Ao todo, seu arquivo .env deve se parecer com isto:
1REACT_APP_PINATA_KEY = <pinata-key>2REACT_APP_PINATA_SECRET = <pinata-secret>3REACT_APP_ALCHEMY_KEY = https://eth-ropsten.alchemyapi.io/v2/<alchemy-key>Agora que temos nossa ABI de contrato e nossa chave de API da Alchemy, estamos prontos para carregar nosso contrato inteligente usando o Alchemy Web3 (opens in a new tab).
Configure seu endpoint e contrato do Alchemy Web3
Primeiro, se você ainda não o tiver, precisará instalar o Alchemy Web3 (opens in a new tab) navegando até o diretório inicial: nft-minter-tutorial no terminal:
1cd ..2npm install @alch/alchemy-web3Em seguida, vamos voltar ao nosso arquivo interact.js. No topo do arquivo, adicione o seguinte código para importar a chave de Alchemy do seu arquivo .env e configure seu Alchemy Web3 endpoint:
1require("dotenv").config()2const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY3const { createAlchemyWeb3 } = require("@alch/alchemy-web3")4const web3 = createAlchemyWeb3(alchemyKey)O Alchemy Web3 (opens in a new tab) é um wrapper em torno do Web3.js (opens in a new tab), fornecendo métodos de API aprimorados e outros benefícios cruciais para facilitar sua vida como desenvolvedor web3. Ele foi projetado para exigir uma configuração mínima, para que você possa começar a usá-la no seu aplicativo imediatamente!
Em seguida, vamos adicionar nosso contrato ABI e endereço do contrato ao nosso arquivo.
1require("dotenv").config()2const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY3const { createAlchemyWeb3 } = require("@alch/alchemy-web3")4const web3 = createAlchemyWeb3(alchemyKey)56const contractABI = require("../contract-abi.json")7const contractAddress = "0x4C4a07F737Bf57F6632B6CAB089B78f62385aCaE"Assim que tivermos ambas as coisas, estaremos prontos para começar a codificar a nossa função "mint"!
Implemente a função mintNFT
Dentro do seu arquivo interact.js, vamos definir nossa função, mintNFT, que, de forma homônima, irá mintar nosso NFT.
Porque vamos fazer numerosas chamadas assíncronas (para o Pinata fixar nossos metadados para IPFS, Alchemy Web3 para carregar o nosso contrato inteligente, e MetaMask para assinar nossas transações), nossa função também será assíncrona.
As três entradas para nossa função serão a url do nosso ativo digital, o name e a description. Adicione a seguinte assinatura da função abaixo da função connectWallet:
1export const mintNFT = async (url, name, description) => {}Tratamento de erros de entrada
Naturalmente, faz sentido ter algum tipo de tratamento de erro de entrada no início da função, então vamos sair desta função se nossos parâmetros de entrada não estiverem corretos. Dentro da nossa função, vamos adicionar o seguinte código:
1export const mintNFT = async (url, name, description) => {2 //tratamento de erros3 if (url.trim() == "" || name.trim() == "" || description.trim() == "") {4 return {5 success: false,6 status: "❗Certifique-se de que todos os campos estão preenchidos antes de mintar.",7 }8 }9}Exibir tudoEssencialmente, se algum dos parâmetros de entrada for uma string vazia, retornamos um objeto JSON onde o booleano success é falso, e a string status informa que todos os campos em nossa UI devem ser preenchidos.
Envie os metadados para o IPFS
Assim que soubermos que nossos metadados estão formatados corretamente, o próximo passo é envolvê-lo em um objeto JSON e enviá-lo para o IPFS através do pinJSONToIPFS que escrevemos!
Para fazer isso, primeiro precisamos importar a função pinJSONToIPFS para nosso arquivo interact.js. No topo do interact.js, vamos adicionar:
1import { pinJSONToIPFS } from "./pinata.js"Lembre-se que o pinJSONToIPFS recebe um corpo JSON. Então, antes de fazer uma chamada para ele, precisaremos formatar nossos parâmetros url, name e description em um objeto JSON.
Vamos atualizar nosso código para criar um objeto JSON chamado metadata e então fazer uma chamada para pinJSONToIPFS com este parâmetro metadata:
1export const mintNFT = async (url, name, description) => {2 //tratamento de erros3 if (url.trim() == "" || name.trim() == "" || description.trim() == "") {4 return {5 success: false,6 status: "❗Certifique-se de que todos os campos estão preenchidos antes de mintar.",7 }8 }910 //criar metadados11 const metadata = new Object()12 metadata.name = name13 metadata.image = url14 metadata.description = description1516 //fazer chamada ao pinata17 const pinataResponse = await pinJSONToIPFS(metadata)18 if (!pinataResponse.success) {19 return {20 success: false,21 status: "😢 Algo deu errado durante o upload do seu tokenURI.",22 }23 }24 const tokenURI = pinataResponse.pinataUrl25}Exibir tudoObserve que armazenamos a resposta da nossa chamada para pinJSONToIPFS(metadata) no objeto pinataResponse. Então, analisamos esse objeto para quaisquer erros.
Se houver um erro, retornamos um objeto JSON onde o booleano success é falso e nossa string status informa que nossa chamada falhou. Caso contrário, extraímos o pinataURL da pinataResponse e o armazenamos como nossa variável tokenURI.
Agora é hora de carregar o nosso contrato inteligente usando a API da Alchemy Web3 que inicializamos no topo do nosso arquivo. Adicione a seguinte linha de código na parte inferior da função mintNFT para definir o contrato na variável global window.contract:
1window.contract = await new web3.eth.Contract(contractABI, contractAddress)A última coisa a adicionar em nossa função mintNFT é a nossa transação Ethereum:
1//configure sua transação Ethereum2const transactionParameters = {3 to: contractAddress, // Obrigatório, exceto durante a publicação de contratos.4 from: window.ethereum.selectedAddress, // deve corresponder ao endereço ativo do usuário.5 data: window.contract.methods6 .mintNFT(window.ethereum.selectedAddress, tokenURI)7 .encodeABI(), //faz uma chamada para o contrato inteligente de NFT8}910//assine a transação via MetaMask11try {12 const txHash = await window.ethereum.request({13 method: "eth_sendTransaction",14 params: [transactionParameters],15 })16 return {17 success: true,18 status:19 "✅ Confira sua transação no Etherscan: https://ropsten.etherscan.io/tx/" +20 txHash,21 }22} catch (error) {23 return {24 success: false,25 status: "😥 Algo deu errado: " + error.message,26 }27}Exibir tudoSe você já está familiarizado com as transações na Ethereum, perceberá que a estrutura é bem parecida com a que você já viu.
- Primeiro, nós configuramos nossos parâmetros de transações.
toespecifica o endereço do destinatário (nosso contrato inteligente)fromespecifica o assinante da transação (o endereço conectado do usuário ao MetaMask:window.ethereum.selectedAddress)datacontém a chamada para o nosso métodomintNFTde contrato inteligente, que recebe o nossotokenURIe o endereço da carteira do usuário,window.ethereum.selectedAddress, como entradas
- Então, fazemos uma chamada de
await,window.ethereum.request, onde pedimos ao MetaMask para assinar a transação. Observe que, nesta solicitação, estamos especificando nosso método eth (eth_SentTransaction) e passando nossostransactionParameters. Neste ponto, a MetaMask irá abrir no navegador e pedirá que o usuário assine ou rejeite a transação.- Se a transação for bem-sucedida, a função retornará um objeto JSON onde o booleano
successé definido como verdadeiro e a stringstatussolicita que o usuário verifique o Etherscan para obter mais informações sobre sua transação. - Se a transação falhar, a função retornará um objeto JSON onde o booleano
successé definido como falso, e a stringstatusretransmite a mensagem de erro.
- Se a transação for bem-sucedida, a função retornará um objeto JSON onde o booleano
No total, nossa função mintNFT deve se parecer com isto:
1export const mintNFT = async (url, name, description) => {2 //tratamento de erros3 if (url.trim() == "" || name.trim() == "" || description.trim() == "") {4 return {5 success: false,6 status: "❗Certifique-se de que todos os campos estão preenchidos antes de mintar.",7 }8 }910 //criar metadados11 const metadata = new Object()12 metadata.name = name13 metadata.image = url14 metadata.description = description1516 //solicitação de fixação do pinata17 const pinataResponse = await pinJSONToIPFS(metadata)18 if (!pinataResponse.success) {19 return {20 success: false,21 status: "😢 Algo deu errado durante o upload do seu tokenURI.",22 }23 }24 const tokenURI = pinataResponse.pinataUrl2526 //carregar contrato inteligente27 window.contract = await new web3.eth.Contract(contractABI, contractAddress) //loadContract();2829 //configure sua transação Ethereum30 const transactionParameters = {31 to: contractAddress, // Obrigatório, exceto durante a publicação de contratos.32 from: window.ethereum.selectedAddress, // deve corresponder ao endereço ativo do usuário.33 data: window.contract.methods34 .mintNFT(window.ethereum.selectedAddress, tokenURI)35 .encodeABI(), //faz uma chamada para o contrato inteligente de NFT36 }3738 //assinar transação via MetaMask39 try {40 const txHash = await window.ethereum.request({41 method: "eth_sendTransaction",42 params: [transactionParameters],43 })44 return {45 success: true,46 status:47 "✅ Confira sua transação no Etherscan: https://ropsten.etherscan.io/tx/" +48 txHash,49 }50 } catch (error) {51 return {52 success: false,53 status: "😥 Algo deu errado: " + error.message,54 }55 }56}Exibir tudoEssa é uma função gigante! Agora, só precisamos conectar nossa função mintNFT ao nosso componente Minter.js...
Conecte o mintNFT ao nosso frontend Minter.js
Abra seu arquivo Minter.js e atualize a linha import { connectWallet, getCurrentWalletConnected } from "./utils/interact.js"; no topo para ser:
1import {2 connectWallet,3 getCurrentWalletConnected,4 mintNFT,5} from "./utils/interact.js"Finalmente, implemente a função onMintPressed para fazer a chamada await para a sua função mintNFT importada e atualize a variável de estado status para refletir se nossa transação foi bem-sucedida ou falhou:
1const onMintPressed = async () => {2 const { status } = await mintNFT(url, name, description)3 setStatus(status)4}Implante seu NFT em um site ativo
Pronto para deixar seu projeto ao vivo para que usuários interajam? Confira este tutorial (opens in a new tab) para implantar seu Mintador em um site ativo.
Um último passo...
Conquiste o mundo da blockchain
Só uma brincadeira, você chegou ao fim do tutorial!
Para recapitular, construindo um minter NFT, você aprendeu com sucesso como:
- Conectar ao MetaMask através do seu projeto frontend
- Chamar métodos de contrato inteligentes no seu frontend
- Assine transações usando MetaMask
Presumivelmente, você gostaria de poder exibir os NFTs mintados através do seu dapp em sua carteira — então, certifique-se de conferir nosso rápido tutorial Como Visualizar seu NFT em sua Carteira (opens in a new tab)!
E, como sempre, se você tiver alguma dúvida, estamos aqui para ajudar no Discord da Alchemy (opens in a new tab). Mal podemos esperar para ver como você aplicará os conceitos deste tutorial em seus projetos futuros!
Última atualização da página: 25 de fevereiro de 2026