WEB3DEV

Cover image for Cunhar NFTs ERC721 na Avalanche
Isabela Curado Nehme
Isabela Curado Nehme

Posted on

Cunhar NFTs ERC721 na Avalanche

Este tutorial irá guiá-lo através dos primeiros passos com um fluxo de trabalho de cunhagem de tokens ERC721 compatível com EVM (máquina virtual da Ethereum) na Avalanche usando Node.js REPL.

Aqui está uma visão geral do que vamos aprender:

Você achará isso bastante útil se estiver migrando da Ethereum ou outra blockchain compatível com EVM e desejar reutilizar seu contrato inteligente de NFT.

Requisitos

Este tutorial pressupõe que você tenha alguma familiaridade com Javascript, terminal Unix e Solidity.

Avalanche X Ethereum

Ao contrário da Ethereum, a Avalanche é uma rede de multi-blockchains, uma das quais executa uma bifurcação (fork) da máquina virtual da Ethereum (EVM) e é compatível com a Ethereum.

A Avalanche é composta por 3 sub-redes: a cadeia X, a cadeia P e a cadeia C.

  • Cadeia X: lida com trocas de valor e executa a Máquina virtual da Avalanche (com namespaced avm).
  • Cadeia P: lida com a plataforma/protocolo (núcleo) e é capaz de criar novas blockchains arbitrárias (com namespaced platform).
  • Cadeia C: a cadeia compatível com a EVM capaz de executar contratos inteligentes em Solidity e dApps. Possui endereços compatíveis com a Ethereum (strings hexadecimais prefixadas com "0x" concatenadas com os 20 bytes mais à direita da chave pública ECDSA de hash Keccak-256).

A maioria das confusões acontece para os iniciantes ao tentar distinguir entre essas diferentes sub-redes. É importante observar que apenas a cadeia C tem compatibilidade com a EVM e endereços compatíveis com a Ethereum. A maioria dos dApps estará interagindo com esta cadeia.

https://docs.avax.network/assets/images/image(21)-3c5cb7f1f21926b05ae3631f453ed49d.png

Agora que aprendemos sobre a infraestrutura da Avalanche, vamos nos preparar para construir!

Configurando

A maneira mais rápida de começar é executar um grupo de nós do simulador localmente. Para fazer isso, siga estas etapas:

Instalar e baixar

  • Instale o Go. Certifique-se de definir a variável $GOPATHpara o local onde você mantém o código Go (ou seja, $HOME/go).
  • Clone o Avalanchego (nó da Avalanche) e o simulador local da Avalanche. Certifique-se de que eles estejam em $GOPATH/src/github.com/ava-labs para que a próxima etapa funcione. Por exemplo, se o meu $GOPATHestiver atualmente definido como ~/mycode/go, o avalanchego e o ava-sim devem estar localizados em ~/mycode/go/src/github.com/ava-labs/avalanchego e ~/mycode/go/src/github.com/ava-labs/ava-sim, respectivamente.
  • Certifique-se de que você tenha o Node.js em seu sistema, baixando-o da página do Node.js. Precisaremos disso para criar o NFT.

Executar nós do simulador local

Para interagir com a blockchain localmente, teremos que executar alguns nós localmente. A Avalanche fornece uma maneira simples de fazer isso com um script de simulador que executa 5 nós em sua máquina local.

Primeiro, construa os programas avalanchego e ava-sim nos repositórios baixados. Eles são projetos Go que precisam ser compilados em programas executáveis. Ambos os projetos incluem um script shell útil que cria automaticamente o projeto localizado em seu diretório raiz em /scripts/build.sh conforme mostrado abaixo (você pode encontrar o script de construção no mesmo local em ava-sim):

avalancego/
├── ...
└──scripts
   ├── ansible
   ├── aws
   ├── build.sh
   ├── ...
   └── versions.sh
Enter fullscreen mode Exit fullscreen mode

Crie cada repositório Go executando o script shell incluído em cada repositório digitando ./scripts/build.sh em seu terminal no nível raiz. O primeiro . é a forma como o Unix executa um executável. Se você receber um erro de seu shell mencionando permissão negada, digite chmod +x scripts/build.sh para transformar o script em um executável.

Em seguida, mude para o repositório ava-sim, execute o simulador com ./scripts/run.sh. Esse script executa os executáveis ​​em avalanchego, portanto, certifique-se de que ele esteja no $GOPATH ao lado do ava-sim. O simulador executa uma rede local de 5 nós escutando em portas diferentes. Neste tutorial, usaremos o nó que escuta na porta 9650.

Se o simulador for executado com sucesso, você deve vê-lo impresso no stdout (dispositivo de saída padrão) semelhante ao mostrado abaixo:

...

INFO [04-06|13:45:41] node/node.go#1051: node version is: avalanche/1.7.1
INFO [04-06|13:45:41] node/node.go#1052: node ID is: NodeID-P7oB2McjBGgW2NXXWVYjV8JEDFoW9xDE5
INFO [04-06|13:45:41] node/node.go#1053: current database version: v1.4.5
INFO [04-06|13:45:41] node/node.go#1051: node version is: avalanche/1.7.1
INFO [04-06|13:45:41] node/node.go#1052: node ID is: NodeID-7Xhw2mDxuDS44j42TCB6U5579esbSt3Lg
INFO [04-06|13:45:41] node/node.go#1053: current database version: v1.4.5
INFO [04-06|13:45:41] node/node.go#489: initializing API server
INFO [04-06|13:45:41] node/node.go#489: initializing API server
INFO [04-06|13:45:41] api/server/server.go#82: API created with allowed origins: [*]
INFO [04-06|13:45:41] api/server/server.go#82: API created with allowed origins: [*]

...
Enter fullscreen mode Exit fullscreen mode

O simulador é executado como um processo em primeiro plano, portanto, abra um novo terminal para continuar.

Bom trabalho! Você está executando nós da Avalanche que consistem em todas as sub-redes em sua máquina local.

Criar um usuário de armazenamento de chaves e adicionar fundos de teste

Para obter algum fundo de teste na AVAX, temos que criar um usuário de armazenamento de chaves usando um nome de usuário e senha no nó de destino (neste caso, é o nó que está em execução na porta 3650). Com seu próprio nome de usuário e senha, envie uma solicitação para este ponto de extremidade:

curl -X POST --data '{
    "jsonrpc":"2.0",
    "id"     :1,
    "method" :"keystore.createUser",
    "params" :{
        "username":"MYUSERNAME",
        "password":"MYPASSWORD"
    }
}' -H 'Content-Type: application/json' 127.0.0.1:9650/ext/keystore
Enter fullscreen mode Exit fullscreen mode

Substitua MYUSERNAME e MYPASSWORD pelo seu próprio nome de usuário e senha, respectivamente.

Importante: você só deve criar um usuário de armazenamento de chaves em um nó que você opera, pois o operador do nó pode acessar sua senha de texto simples.

Observe a seguinte chave privada pré-financiada (chamada de chave "ewoq" no documento da Avalanche), que é uma chave privada fornecida para obter financiamento para sua conta local convenientemente. Vamos importar essa chave privada para um endereço de cadeia C:

PrivateKey-ewoqjP7PxY4yr3iLTpLisriqt94hdyDFNgchSxGGztUrTXtNN
Enter fullscreen mode Exit fullscreen mode
curl --location --request POST '127.0.0.1:9650/ext/bc/C/avax' 
--header 'Content-Type: application/json' \
--data-raw '{
    "method": "avax.importKey",
    "params": {
        "username":"MYUSERNAME",
        "password":"MYPASSWORD",
        "privateKey":"PrivateKey-ewoqjP7PxY4yr3iLTpLisriqt94hdyDFNgchSxGGztUrTXtNN"
    },
    "jsonrpc": "2.0",
    "id": 1
}'
Enter fullscreen mode Exit fullscreen mode

Observe o C no URI da rota que indica a cadeia com a qual queremos interagir? Nós só nos interessa a cadeia C porque estamos cunhando um NFT ERC721. No entanto, no futuro, você pode e estará trabalhando com outras cadeias usando a API pública.

Leia mais em: Funding a Local Network (Financiando uma rede local).

Integrar com a Metamask

Configure a Metamask para se conectar a um endereço RPC personalizado da rede local:

Configurações de rede de teste local (Simulador local da Avalanche)

Nome da rede: Avalanche Local
Novo URL RPC: http://localhost:9650/ext/bc/C/rpc (para a cadeia C)
ID da cadeia: 43112
Símbolo: AVAX
Explorador: N/A

Você pode verificar as configurações para a rede de teste e a rede principal aqui.

Rede de teste local: a porta de escuta pode não ser a 9650, dependendo se você executa um programa de exemplo no nó avalanchego ou no script de execução avalanche-simulator. Este último é recomendado para início rápido e terá um nó de escuta na porta 9650.

Crie uma nova conta na Metamask importando esta chave privada 0x56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027 fornecida na Metamask, que é uma conta de teste apenas para testes locais.

Se tudo correr bem, você deve ter uma carteira da Metamask AVAX financiada para construir seu aplicativo localmente:

https://i.imgur.com/fkLXV17.png

Nó público da API

A Avalanche mantém um gateway de API público, que você pode usar no desenvolvimento rápido sem precisar executar seu próprio nó.

Criar um projeto de nó

Com o Node.js já instalado, digite o seguinte na linha de comando para criar um diretório de projeto:

mkdir hello-avax && cd hello-avax
npm init --yes
Enter fullscreen mode Exit fullscreen mode

Em seguida, no diretório hello-avax, instale alguns pacotes com:

npm install --save-dev hardhat
Enter fullscreen mode Exit fullscreen mode

Em seguida, digite npx hardhat no diretório raiz. O CLI do Hardhat deve imprimir algumas opções para configurar o projeto:

npx hardhat

> 888    888                      888 888               888
> 888    888                      888 888               888
> 888    888                      888 888               888
> 8888888888  8888b.  888d888 .d88888 88888b.   8888b.  888888
> 888    888     "88b 888P"  d88" 888 888 "88b     "88b 888
> 888    888 .d888888 888    888  888 888  888 .d888888 888
> 888    888 888  888 888    Y88b 888 888  888 888  888 Y88b.
> 888    888 "Y888888 888     "Y88888 888  888 "Y888888  "Y888
>
> 👷 Bem vindos ao Hardhat v2.9.3 👷‍
>
> ? O que você quer fazer? …
> ❯ Criar um projeto de amostra básico
>   Criar um projeto de amostra avançado
>   Criar um projeto de amostra avançado que usa TypeScript
>   Criar um hardhat.config.js vazio
>   Sair
Enter fullscreen mode Exit fullscreen mode

Selecione a primeira opção "Criar um projeto de amostra básico (create a basic sample project)" e escolha "Y" para todas as perguntas. O Hardhat irá instalar todas as dependências necessárias e criar alguns diretórios para você, como /contracts e /scripts.

Em seguida, copie esta configuração e cole-a no hardhat.config.js no nível raiz e salve-a:

// hardhat.config.js
import { task } from "hardhat/config"
import { BigNumber } from "ethers"
import "@nomiclabs/hardhat-waffle"

const FORK_FUJI = false
const FORK_MAINNET = false
const forkingData = FORK_FUJI ? {
  url: 'https://api.avax-test.network/ext/bc/C/rpc',
} : FORK_MAINNET ? {
  url: 'https://api.avax.network/ext/bc/C/rpc'
} : undefined

task("accounts", "Imprime a lista de contas", async (args, hre) => {
  const accounts = await hre.ethers.getSigners()
  accounts.forEach((account) => {
    console.log(account.address)
  })
})

task("balances", "Imprime a lista de saldos das contas AVAX", async (args, hre) => {
  const accounts = await hre.ethers.getSigners()
  for (const account of accounts){
    const balance = await hre.ethers.provider.getBalance(
      account.address
    );
    console.log(`${account.address} has balance ${balance.toString()}`);
  }
})

export default {
  solidity: {
    compilers: [
      {
        version: "0.5.16"
      },
      {
        version: "0.6.2"
      },
      {
        version: "0.6.4"
      },
      {
        version: "0.7.0"
      },
      {
        version: "0.8.0"
      },
      {
        version: "0.8.1"
      }
    ]
  },
  networks: {
    hardhat: {
      gasPrice: 225000000000,
      chainId: !forkingData ? 43112 : undefined,
    },
    local: {
      url: 'http://localhost:9650/ext/bc/C/rpc',
      gasPrice: 225000000000,
      chainId: 43112,
      accounts: [
        "0x56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027",
        "0x7b4198529994b0dc604278c99d153cfd069d594753d471171a1d102a10438e07",
        "0x15614556be13730e9e8d6eacc1603143e7b96987429df8726384c2ec4502ef6e",
        "0x31b571bf6894a248831ff937bb49f7754509fe93bbd2517c9c73c4144c0e97dc",
        "0x6934bef917e01692b789da754a0eae31a8536eb465e7bff752ea291dad88c675",
        "0xe700bdbdbc279b808b1ec45f8c2370e4616d3a02c336e68d85d4668e08f53cff",
        "0xbbc2865b76ba28016bc2255c7504d000e046ae01934b04c694592a6276988630",
        "0xcdbfd34f687ced8c6968854f8a99ae47712c4f4183b78dcc4a903d1bfe8cbf60",
        "0x86f78c5416151fe3546dece84fda4b4b1e36089f2dbc48496faf3a950f16157c",
        "0x750839e9dbbd2a0910efe40f50b2f3b2f2f59f5580bb4b83bd8c1201cf9a010a"
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Agora, com os nós do simulador local ainda em execução, execute os seguintes comandos:

npx hardhat accounts --network local
> 0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC
> 0x9632a79656af553F58738B0FB750320158495942
> 0x55ee05dF718f1a5C1441e76190EB1a19eE2C9430
> ...
Enter fullscreen mode Exit fullscreen mode

Espero que você veja alguns endereços impressos. Em seguida, verifique os saldos com:

npx hardhat balances --network local
> 0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC has balance 50000000000000000000000000
> 0x9632a79656af553F58738B0FB750320158495942 has balance 0
> 0x55ee05dF718f1a5C1441e76190EB1a19eE2C9430 has balance 0
> ...
Enter fullscreen mode Exit fullscreen mode

Se você tiver criado corretamente um usuário de armazenamento de chaves e adicionado fundos de teste, deverá ver um dos endereços ricos (wealthy addresses) mostrados acima.

Desenvolver um contrato inteligente de NFT

Se você já possui um contrato inteligente compatível com a EVM para cunhagem de NFTs, pode pular esta seção. No entanto, é muito divertido acompanhar!

Vamos criar tokens não fungíveis ERC721 com seus próprios atributos. Para simplificar, qualquer conta poderá chamar um método mintTo para cunhar itens.

Estaremos usando o contrato inteligente ERC721 padrão do Open Zeppelin. Instale-o em seu projeto com o npm install @openzeppelin/contracts.

No diretório /contracts, remova o arquivo Greeter.sol gerado, crie um novo arquivo chamado Filet.sol e digite o código a seguir (opcionalmente, você pode copiá-lo e colá-lo, mas estará perdendo a oportunidade de exercitar suas habilidades programação):

// contracts/Filet.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract Filet is ERC721URIStorage {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    constructor() ERC721("Filet", "FILET") {}

    function mintTo(address player, string memory tokenURI)
        public
        returns (uint256)
    {
        _tokenIds.increment();
        uint256 newItemId = _tokenIds.current();
        _mint(player, newItemId);
        _setTokenURI(newItemId, tokenURI);

        return newItemId;
    }
}
Enter fullscreen mode Exit fullscreen mode

Como estamos implementando o ERC721URIStorage para o nosso contrato de NFT, obtemos o método especial _setTokenURI(tokenId, tokenURI) gratuitamente sobre a interface regular do ERC721. O ERC721URIStorage é uma extensão da interface IERC721 com a conveniente capacidade de configurar e obter o URI de metadados para cada token. Obteremos esse URI carregando o ativo em nft.storage.

Você pode criar seu próprio nome de contrato, nome de token e símbolo de token em vez de "Filet", mas certifique-se de usar o nome de forma consistente.

Agora compile o contrato Filet.sol usando este comando do hardhat em seu nível raiz:

npx hardhat compile
> Compiling 1 file with 0.8.0
> Compilation finished successfully
Enter fullscreen mode Exit fullscreen mode

Em seguida, crie um arquivo de script nomeado deploy.js no diretório scriptse digite o seguinte código antes de salvá-lo:

import {
  Contract,
  ContractFactory
} from "ethers"
import { ethers } from "hardhat"

const deploy = async (contractName) => {
  const Contract = await ethers.getContractFactory(contractName)
  const contract = await Contract.deploy()

  await contract.deployed()
  console.log(`${contractName} deployed to: ${contract.address}`)
}

const main = async () => {
  await deploy("Filet")
}

main()
.then(() => process.exit(0))
.catch(error => {
  console.error(error)
  process.exit(1)
})
Enter fullscreen mode Exit fullscreen mode

Este script usa a biblioteca de Ethers para implantar o contrato no(s) nó(s) da Avalanche local(is). Agora, podemos implantar o contrato com este comando do Hardhat:

npx hardhat run scripts/deploy.js --network local
> Filet deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3
Enter fullscreen mode Exit fullscreen mode

Agora temos um contrato de token implantado em 0x5FbDB2315678afecb367f032d93F642f64180aa3. Anote esta saída de endereço, pois a sua provavelmente será diferente.

Interagindo com o contrato

Agora, vamos ativar o console do desenvolvedor do Hardhat para começar a brincar com nosso contrato Filet de forma interativa:

npx hardhat console --network local
> Welcome to Node.js v14.18.1.
> Type ".help" for more information.
> >
Enter fullscreen mode Exit fullscreen mode

Ao interagir com o prompt do console, estamos aprendendo cada etapa de maneira discreta, sem nos distrairmos com a interface do usuário.

Agora, digite o seguinte no prompt para inicializar o objeto do contrato (lembre-se de usar seu endereço de contrato em vez do mostrado abaixo):

>> const Filet = await ethers.getContractFactory("Filet")
>> const filet = await Filet.attach("0x5FbDB2315678afecb367f032d93F642f64180aa3")
Enter fullscreen mode Exit fullscreen mode

Em seguida, examinamos as contas e os saldos:

>> const accounts = await ethers.provider.listAccounts()
>> accounts
> [
>   '0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC',
>   '0x9632a79656af553F58738B0FB750320158495942',
>   '0x55ee05dF718f1a5C1441e76190EB1a19eE2C9430',
>   // ...
> ]
Enter fullscreen mode Exit fullscreen mode

O array, sem surpresa, deve conter todos os endereços listados com o comando anterior npx hardhat accounts. Vamos selecionar um dos endereços para examinar o saldo do token FILET (estamos escolhendo a segunda conta).

>> const balance = (await filet.balanceOf(accounts[1])).toString()
> 0
Enter fullscreen mode Exit fullscreen mode

Obviamente, o endereço 0x9632a79656af553F58738B0FB750320158495942, pertencente à segunda conta com a qual acessamos com o accounts[1], não possui nenhum token FILET. Antes de podermos cunhar um token para o endereço, teremos que carregar os metadados, como a imagem, o nome e a descrição que queremos vincular ao token para nft.storage.

Carregando o ativo e cunhando o token

Antes de continuarmos, instale as bibliotecas nft.storage e mime no nível raiz do projeto com npm install nft.storage mime --save.

Agora, faremos o upload dos metadados de um NFT - imagem, nome e descrição - para nft.storage e usaremos o URI IPFS resultante no mintTo. Para escrever um script que possa ser importado e executado no nó REPL do Hardhat, crie um arquivo chamado upload.mjs dentro do diretório /scripts com o seguinte código (substituindo a variável NFT_STORAGE_KEYpela sua própria chave API).

import { NFTStorage, File } from 'nft.storage'

import mime from 'mime'

import fs from 'fs'

import path from 'path'

const NFT_STORAGE_KEY = 'YOUR_OWN_API_KEY'

async function storeNFT(imagePath, name, description) {
    const image = await fileFromPath(imagePath)

    const nftstorage = new NFTStorage({ token: NFT_STORAGE_KEY })

    return nftstorage.store({
        image,
        name,
        description,
    })
}

async function fileFromPath(filePath) {
    const content = await fs.promises.readFile(filePath)
    const type = mime.getType(filePath)
    return new File([content], path.basename(filePath), { type })
}

async function upload(imagePath, name, description) {
    const result = await storeNFT(imagePath, name, description)
    return result
}

export { upload }
Enter fullscreen mode Exit fullscreen mode

Retorne ao nó REPL e importe a função upload do script. Copie um arquivo de imagem de sua escolha para o diretório raiz para usar como a imagem do NFT.

>> const { upload } = await import("./scripts/upload.mjs")
>> const result = await upload("./pickleheart.png", "Pickleheart", "Image of Pickleheart Filet")
>> result
> Token {
>   ipnft: 'bafyreicb3ewk33keh77mwxhmhdafxsjlkflichr2mjnyim6tbq3qjkwcue',
>   url: 'ipfs://bafyreicb3ewk33keh77mwxhmhdafxsjlkflichr2mjnyim6tbq3qjkwcue/metadata.json'
> }
Enter fullscreen mode Exit fullscreen mode

Observe que recebemos a resposta como um objeto Token com a propriedade url. Podemos usar este URI fornecido para cunhar nosso primeiro NFT para outra conta:

>> const _tx = await filet.mintTo(accounts[1], result.url)
Enter fullscreen mode Exit fullscreen mode

Em seguida, verifique o saldo da conta receptora:

>> const balance = (await filet.balanceOf(account[1])).toString()
>> balance
> 1
Enter fullscreen mode Exit fullscreen mode

Além disso, podemos verificar o proprietário atual do NFT chamando ownerOf(tokenId). Como cunhamos apenas o primeiro token, o tokenId é 1:

>> const tokenId = 1
>> const ownerAddress = (await filet.ownerOf(tokenId))
>> ownerAddress === account[1].address
> true
Enter fullscreen mode Exit fullscreen mode

O endereço de recebimento agora possui 1 FILET. Também confirmamos que ele é o proprietário do primeiro NFT.

Por padrão, ethers usa o primeiro endereço como o signatário da transação, portanto, é o account[0] que assina a cunhagem quando chamamos mintTo(recipientAddress, tokenURI.

💡 O que foi devolvido por mintTo(...)?
Você pode se perguntar por que, quando chamamos mintTo(...) anteriormente, atribuímos o valor retornado a uma variável não utilizada chamada _tx, em vez de usar o ID do token UInt256 retornado esperado na próxima chamada, para o ownerOf(tokenId) examinar o proprietário do token.
Isso ocorre porque o valor de retorno de uma função Solidity não pura ou sem visualização está disponível apenas quando a função é chamada na cadeia (do mesmo contrato ou de outro contrato). Em nosso caso, nosso pequeno prompt do Hardhat é um cliente fora da cadeia. Por exemplo, o método balanceOf(...) é uma função de visualização porque só lê a partir da cadeia.
Quando uma função não pura ou sem visualização é chamada fora da cadeia, o valor de retorno é sempre o hash da transação, não o valor de retorno pretendido da função. Portanto, quando chamamos mintTo(...), recebemos o objeto de transação e não o ID do token que esperávamos. Nós o atribuímos a _tx para enfatizar que era um objeto de transação (tx) e o precedemos com um sublinhado para enfatizar que não estávamos utilizando-o.
Saiba mais sobre funções puras e de visualização e sobre inscrição em um evento para obter o valor retornado. Além disso, confira o método callStatic, que é útil para testar suas chamadas e valores retornados.

Recuperando os metadados de um token

A etapa final é recuperar os metadados de cada token do IPFS para que você possa exibir a imagem, o nome e a descrição do token na web. Com o ERC721Storage, podemos chamar tokenURI(uint256 tokenId) para recuperar o tokenURI armazenado na cadeia:

>> let ipfsURI = await filet.tokenURI(tokenId.toNumber())
>> ipfsURI
> 'ipfs://bafyreicb3ewk33keh77mwxhmhdafxsjlkflichr2mjnyim6tbq3qjkwcue/metadata.json'
Enter fullscreen mode Exit fullscreen mode

Como existem navegadores que ainda não suportam URLs IPFS de forma nativa, bem como a API fetch padrão, o cliente do Javascript nft.storage inclui uma função auxiliar que converte esse URI IPFS em uma versão HTTPS por meio do gateway nftstorage.link. No console, você pode importar esta função toGatewayURL do nft.storage:

>> const { toGatewayURL } = await import("nft.storage")
>> const { href } = await toGatewayURL(ipfsURI)
>> href
> 'https://nftstorage.link/ipfs/bafyreicb3ewk33keh77mwxhmhdafxsjlkflichr2mjnyim6tbq3qjkwcue/metadata.json'
Enter fullscreen mode Exit fullscreen mode

🎉 Parabéns! Você aprendeu a construir uma loja NFT na Avalanche. Agora vá em frente e conquiste o mundo!

Esse artigo foi escrito pela NFT School e traduzido por Isabela Curado Nehme. Seu original pode ser lido aqui.

Top comments (0)