Intro
Ao final desse artigo você aprenderá como programar em Javascript uma aplicação para armazenar metadados de NFTs usando IPFS e fixando na rede decentralizada da Filecoin.
Projeto pronto no GitHub.
Metadados
NFTs possuem metadados que podem ser acessados chamando funções em seu smart contract. Estes metadados podem ser inseridos de diversas formas - variando de contrato em contrato, sua utilidade mais comum é a de redirecionar um link para algum tipo de conteúdo, um aspecto da informação que há representa – na maioria dos casos, será uma imagem.
IPFS
“IPFS é uma estrutura peer-2-peer que registra endereços únicos para cada tipo de conteúdo.”
A frase acima foi salva em um arquivo de texto e adicionada ao IPFS, onde este separou o arquivo em vários pequenos ‘pedaços’ criptografados (hashes), gerou-se um identificador único, o identificador de conteúdo (CID), que atribui ao documento o CID:
QmQfbiQk9rC7HjyT4maVPsu1xPz8nXTTiHdCeYfNXBpA93
Acessando através do _gateway _do IPFS vamos requisitar o arquivo de texto. Repare que o mesmo conteúdo será obtido...
https://ipfs.io/ipfs/QmQfbiQk9rC7HjyT4maVPsu1xPz8nXTTiHdCeYfNXBpA93
Quando o gateway recebe uma requisição para fazer download de um CID, o provedor vai requisitar a seus pares por onde o conteúdo está sendo referenciado, vai baixar uma cópia para si e tornar-se mais um provedor do conteúdo.
Os provedores (nodes) armazenam conteúdos de interesse de usuários que requisitaram o serviço de fixação (pinning). Estes podem manter o serviço ativo por muito tempo ou descartar em caso de não estar sendo usado e for necessário liberar espaço.
NFT Storage
NFT Storage é um serviço de armazenamento de longo prazo para informações de NFTs como, metadado, imagens, atributos e mais.
Um fantástico protocolo completamente útil e gratuito que administra as indexações em servidores dedicados e as decentraliza na Filecoin.
Eles constam com ajuda de uma interface em seu website onde o serviço pode ser executado sem o uso de programação.
Mas não estamos aqui para isso não é mesmo?! Viemos montar uma aplicação e entender o que acontece embaixo dessa interface.
Modelo Padrão de Metadado
Para garantir que as informações únicas do seu NFT sejam reconhecidas pelos provedores, há um modelo a ser seguido, embora não seja obrigatório, é recomendável para maior portabilidade na web3. Este modelo fornecido abaixo foi trazido pela documentação da OpenSea.
{
"description": "Friendly OpenSea Creature that enjoys long swims in the ocean.",
"external_url": "https://openseacreatures.io/3",
"image": "https://storage.googleapis.com/opensea-prod.appspot.com/puffs/3.png",
"name": "Dave Starbelly",
"attributes": [
{
"display_type": "boost_percentage",
"trait_type": "Stamina Increase",
"value": 10
},
{
"display_type": "number",
"trait_type": "Generation",
"value": 2
}
],
}
Preparação de ambiente
Instale node.js, pode ser a última versão.
Inicialize um repositório pelo gitHub e acesse o terminal do seu editor, no meu caso o VSCode, então:
mkdir web3-nft-storage
cd web3-nft-storage
npm init
Instale a API da NFT Storage e Dotenv para gerenciarmos a API Key como variável de ambiente:
npm install nft.storage
npm install dotenv
Implementação
Há vários jeitos de modificar a estrutura a ser proposta para adaptar as necessidades individuais de cada aplicação. Mas nesse caso vamos propor a linearidade das seguintes atividades:
- Criar conta na NFT Storage e adquirir a API Key;
- Fazer upload das imagens, retornando seu CID e fixando na Filecoin através do provedor da NFT Storage;
- Adicionar o link para a imagem no metadado do NFT, onde estão as características extras que seguem o modelo padrão e repetir o upload da 2 etapa;
- Conferindo resultados;
- Considerações finais;
Criando conta e buscando a chave API
As chaves da NFT Storage são gratuitas e cada conta tem um máximo de 31GB.
Acesse o site https://nft.storage/;
Cadastre-se;
Copie a chave API criada em https://nft.storage/manage/;
Crie um arquivo chamado ".env";
Transcreva a linha abaixo substituindo APIKEY pela chave copiada:
NFT_STORAGE=APIKEY
Fazendo upload da imagem e metadado
Crie uma pasta chamada "image" e coloque as imagens;
Crie uma pasta chamada "metadata" e coloque o metadado do seu NFT sem o campo "image". O código irá gerar o CID para a imagem e adicionar o campo;
Crie uma pasta chamada "out";
Crie um arquivo chamado "upload.mjs" dentro da pasta "scripts" e copie o código abaixo:
import { NFTStorage, File, Blob } from "nft.storage"
import fs from 'fs'
import dotenv from 'dotenv'
dotenv.config()
const API_KEY = process.env.NFT_STORAGE
const totalImages = 1 // Substitua pelo total de imagens
async function uploadImageToNFTStorage(i) {
const image = new File(
[await fs.promises.readFile('images/web3dev-'+i+'.png')],
{ type: 'image/png' }
);
const client = new NFTStorage({ token: API_KEY })
const cidImage = await client.storeBlob(image)
return cidImage
}
async function tryToUploadImg(i) {
console.log("Uploading id:", i)
try {
const cidImage = await uploadImageToNFTStorage(i)
return cidImage
} catch (err) { console.error(err) }
return tryToUploadImg(i)
}
async function tryToUploadMeta(cidImage, index) {
const metadado = await fs.readFileSync('metadata/web3dev-'+index+'.json', 'utf8')
let jsonRetrieved = await JSON.parse(metadado)
try {
let data = {
id: jsonRetrieved['id'],
image: 'https://ipfs.io/ipfs/'+cidImage,
name: jsonRetrieved['name'],
attributes: jsonRetrieved['attributes']
}
const client = new NFTStorage({ token: API_KEY })
const blobCreated = new Blob([JSON.stringify(data)]);
const cidMetadata = await client.storeBlob(blobCreated);
return cidMetadata
} catch (err) { console.log(err) }
return tryToUploadMeta(cidImage, index)
}
async function main() {
const fileSream = fs.createWriteStream('out/cid-metadata.json')
for(let i = 1; i < totalImages+1; i++){
let cidImage = await tryToUploadImg(i)
let cidMetadata = await tryToUploadMeta(cidImage, i)
let data = {
id: i,
cid: cidMetadata
}
fileSream.write(JSON.stringify(data)+'\n')
}
}
main().catch((error) => { console.error(error); process.exit(1);})
Recentemente nesse Tweet, NFT Storage anunciou ter elevado a velocidade de carregamento e também reduzido tempo de upload em 99% em relação aos uploads mais demorados.
Dependendo do total de requisições a serem feitas, é possível que nosso código não termine de executar. Como não queremos isso, vamos utiliziar de Try Catches e WriteFileStreams para que nosso progresso não seja perdido por conta de qualquer situação desafortunada.
Execução
Com suas imagens e respectivos metadados nas pastas específicadas, a API registrada no .env, podemos executar o código chamando:
node scripts/upload.mjs
Copie o CID gerado em out/cid-metadata.json e adicione na frente:
https://ipfs.io/ipfs/
No meu caso este foi o resultado:
https://ipfs.io/ipfs/bafkreifgxn53znac52tf5bvjv2sbbttfaz67trtftpmbxt7qk2ziforyia
Retornando a estrutura do metadado do NFT de id 1:
{"id":"1","image":"https://ipfs.io/ipfs/bafybeigifzcy4bujan3tj4p2gpnofqpdrbut5yh6zlskbqg2t3onsyfa4i","name":"Web3Dev","attributes":[{"trait_type":"Tier","value":"1"}]}
Acessando o link em "image" temos por fim a imagem do nosso NFT:
Considerações
Agora o NFT Storage vai gerenciar seus provedores com recursividade dentro da Filecoin para, expandir o tempo de vida das suas imagens registradas mesmo que, não haja constantes requisições de seus hashes mantendo múltiplos caches atualizados.
O produto final desse processo é a lista de CID's. Os identificadores únicos serão utilizados pela função em solidity setTokenURI e TokenURI.
Lembre-se de criar um arquivo .gitignore na pasta raiz e adicionar o arquivos e pastas que não devem ser salvos no gitHub, como por exemplo a sua API privada da NFT Storage e alguns arquivos pesados como a pasta node_modules.
Top comments (0)