Tutorial de Smart Contract ERC-1155 (Multitoken) com Solidity - Parte 2

Web3 e Blockchain

Tutorial de Smart Contract ERC-1155 (Multitoken) com Solidity - Parte 2

Luiz Duarte
Escrito por Luiz Duarte em 18/05/2023
Junte-se a mais de 34 mil devs

Entre para minha lista e receba conteúdos exclusivos e com prioridade

Recentemente comecei um tutorial de programação de smart contract multi-token em Solidity, usando o padrão ERC-1155 aqui no blog e esta é a segunda parte deste tutorial. Caso não tenha implementado a primeira parte ainda, ela é obrigatória e pode ser encontrada neste link.

Na primeira parte implementamos um contrato MyTokens e todas as funções e eventos obrigatórios do padrão. No entanto o padrão é beeem minimalista, o que quer dizer que ele apenas determina como se dará a interação para transferência, delegação e verificação de saldo dos tokens registrados no contrato. Ele não determina por exemplo como você pode mintar/cunhar tokens, destrui-los, impedir o minting de novos e por aí vai, tarefas bem comuns nesse meio.

Além disso, você deve ter sentido falta do conteúdo dos tokens em si, uma imagenzinha pelo menos. Afinal, tudo o que registramos até o momento foram tokens representados por ids. Mas se eu sou o dono do token de id 1, o que afinal isso representa? Uma imagem? Um MP3? Um imóvel físico? O padrão define como os metadados podem ser armazenados e falaremos disso também nesta parte 2.

Vamos lá!

Minting

Duas tarefas muito comuns em contratos de tokens são o Minting e o Burning. Mas o que são eles? Vamos começar explicando minting e futuramente volto no Burning.

Minting é o ato de cunhar tokens e usamos esta palavra ao invés de criar ou gerar em alusão ao processo físico de cunhagem de moedas, muito usado na antiguidade. Mintar um token nada mais é do que criá-lo de fato, geralmente já transferindo a sua propriedade para alguém. E embora uma função de mint não esteja presente no padrão ERC-1155, ela é quase onipresente nos contratos deste tipo. Mas antes de partir para o minting, vamos definir algumas novas variáveis de estado em nosso contrato.

Em um cenário 100% NFT, de tokens não fungíveis, eu poderia assumir que cada id possui apenas uma unidade disponível, certo? Em um cenário 100% fungível (ERC20) eu poderia usar apenas um id e definir (ou não) um total supply, certo? E em um cenário semi-fungível multi-token, como fazemos? Neste caso descrevemos o supply máximo de cada tipo de token em uma estrutura de dado, como fiz com o array _currentSupply. Usando o próprio id do token como índice, é possível consultar o supply disponível de cada tipo de token.

E por último, defini o preço base de um token do nosso contrato, ou seja, o quanto vamos cobrar de quem quiser adquirir uma unidade, tendo abaixo uma sugestão de como implementar essa função.

Começamos com algumas validações básicas se o id do token é válido, se o supply máximo do token já não foi atingido e se o requisitante está pagando o valor exigido pelo mesmo. Tudo dando certo, nós assumimos que o msg.sender será o dono do token mintado, por isso adicionamos o token no mapping de _balances, decrementamos o supply disponível para aquele tipo de token e emitimos o evento de transferência, para sinalizar (aprenda a escutar eventos aqui).

Entenda esta função de mint com URI apenas como uma sugestão pois ela pode variar enormemente.

A função acima te dá uma ideia de como mintar, mas não ajuda muito quando o assunto é o quê mintar. Se eu quiser registrar uma foto? Um MP3? Como faço? Nesses casos, embora tecnicamente seja possível salvar os bytes do arquivo digital na blockchain, o mais comum é salvarmos apenas a URL do arquivo em questão, que estará armazenado em outro local (geralmente IPFS), para economizar nas transações, principalmente de grandes coleções. Essa abordagem é tão comum que é prevista na especificação 1155 como um opcional, definido pela interface abaixo (copie e cole no seu arquivo MyTokens.sol, junto das demais interfaces).

Essa interface define que os contratos multi-token devem possuir uma função que, dado um token id, retorne a URL para um JSON com os metadados do mesmo, cujo formato também é sugerido na especificação como sendo abaixo.

Resumindo: o JSON do token deve ter as informações pertinentes ao mesmo, o que for necessário, e deve ser hospedado publicamente na Internet (sendo que o mais comum é utilizar a IPFS para isso). Se você já trabalhou com a ERC721 antes não deve ter visto nenhuma novidade aqui.

Curso Web3 para Iniciantes

Implementando Metadados

Para servir de exemplo de implementação de metadados (o JSON que citei acima), vamos adicionar a função exigida pela extensão. Eu não vou mostrar aqui como criar o JSON ou como hospedá-lo, pois foge do escopo do tutorial. Vou partir do pressuposto que você já possui esses JSON em algum lugar ou que vai criá-los posteriormente.

Primeiro, adicione a interface ERC1155Metadata_URI no seu MyTokens.sol, junto das outras interfaces. Depois, ajuste o seu contrato MyTokens para adicionar a herança dessa interface também, bem como temos de ajustar a função supportsInterface para sinalizar que estamos suportando também a nova interface.

Depois, o próximo passo é implementarmos a função obrigatória da nova interface: uri.

Essa URL vai ser gerada automaticamente toda vez que a função for chamada, usando uma URL base, o id do token e a função JSON. Atenção que estou usando aqui a função string.concat, disponível a partir da versão 0.8.12 do Solidity, e a função Strings.toString, da biblioteca Strings.sol da OpenZeppelin, que você deve importar no seu contrato como abaixo (atenção que se estiver usando HardHat deve instalar a lib de contratos do OpenZeppelin e importá-la de acordo).

Essa biblioteca tem várias funções utilitárias muito úteis para manipulação de strings.

Opcionalmente você pode incluir no seu contrato funções para mudar a URL dos metadados, caso mude o local onde os arquivos JSON estejam armazenados. Mas se fizer isso, a especificação exige que você dispare um evento específico de mudança de URI, então esteja atento.

Burning

Outra função extremamente relevante para alguns projetos é a de burning. Burning é o ato de destruir (queimar, no inglês) um token por qualquer que seja o motivo. Na nossa estrutura de contrato pode implementar uma função de burn da seguinte maneira.

Comecei fazendo uma série de verificações, sendo que só recebo o dono do token e o id do token a ser queimado:

  • primeiro, o dono do token não pode ser zero;
  • segundo, o id do token deve ser válido;
  • terceiro, o dono do token deve ter pelo menos uma unidade do mesmo (vamos queimar uma de cada vez nesse exemplo);
  • quarto, ele deve ser o requisitante do burn ou ter dado permissão ao requisitando (approveForAll);

Após essas verificações todas, eu reduzo o balance do dono do token e emito o evento que sou obrigado pelo padrão ERC1155, avisando que houve uma transferência ligada à carteira de endereço zero.

E com isso finalizamos mais esta etapa no desenvolvimento de nosso contrato multi-token. Conseguimos cobrir o principal nessas duas partes mas o céu é o limite quando o assunto é smart contract, então tendo qualquer dúvida, deixe nos comentários.

Agora é a hora de você aprender como os profissionais fazem contratos ERC-1155, o que vou abordar no próximo tutorial, que você confere neste link. E se quiser aprender a reduzir o consumo de gás nos seus smart contracts, este é o artigo certo.

TAGS:

Olá, tudo bem?

O que você achou deste conteúdo? Conte nos comentários.

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *