WEB3DEV

Cover image for Como criar NFTs On-chain na Solidity
Fatima Lima
Fatima Lima

Posted on

Como criar NFTs On-chain na Solidity

Incorporação do conteúdo do usuário em um NFT na cunhagem

Image description

Fonte: https://2muchcoffee.com/blog/how-to-build-an-opensea-like-nft-marketplace/

Tendo passado algum tempo desde que escrevi algo relacionado à blockchain ou NFT, achei que uma atualização seria conveniente.

No momento em que escrevi este meu primeiro artigo sobre NFT,

Criando NFTs de Arte Generativa com Python e Solidity, teve mais de 50k de visualizações; muitas das quais resultaram em indivíduos, empresas e times de desenvolvedores se aproximando do desenvolvimentos e serviços de consultoria de blockchains. Graças aos leitores que compartilham estes artigos, tive a oportunidade de trabalhar em vários projetos interessantes - hoje gostaria de compartilhar os detalhes de um desses projetos em particular. O cliente solicitou, neste caso, o desenvolvimento de um NFT inteiramente on-chain com a capacidade extra de adicionar conteúdo de usuário no momento da cunhagem.

NFTs Off-chain

Isto levanta a questão, não são todos os NFTs on-chain? Bem, qualquer pessoa que tenha trabalhado em um projeto de NFT na blockchain Ethereum pode dizer que há limitações reais com relação ao armazenamento de dados on-chain; isto é proibitivamente caro e a maioria dos projetos de NFT armazenam arquivos de imagem e metadados off-chain. Normalmente, os únicos dados que vemos armazenados "on-chain" são um hash imutável para nossos metadados (nosso tokenURI) - A maneira mais fácil de descrever este hash é um link para nosso NFT real, a diferença aqui é que um hash depende dos dados a partir dos quais é criado; altere a fonte, e o hash não é mais válido, portanto é imutável. Para manter as coisas 'descentralizadas' soluções de armazenamento off-chain, como IPFS e Arweave, fazem o 'levantamento de peso' com respeito ao armazenamento descentralizado de dados do NFT, alavancando uma 'comunidade' de nós ao redor do mundo - qualquer um pode executar um nó IPFS assim como qualquer um pode executar um minerador da blockchain ou um nó de consenso.

Agora, você pode estar familiarizado com projetos 'on-chain' como o Loot, então como isso é feito? Podemos realmente armazenar mídia visual adequada on-chain?

Bem, a resposta é sim, mas isso requer algumas coisas, a saber, a codificação Base64 e o tipo de imagem SVG. Ambos nos permitem lidar com dados textuais em vez dos típicos dados visuais 'pesados' de memória, como PNG ou JPEG. Isto significa que precisamos fazer duas coisas:

1 — Codificar em Base64 nosso json metatdata

2 — Codificar ‘instruções’ para renderização de imagens no formato SVG

Felizmente para nós, os navegadores podem entender ambos os formatos e mercados baseados em navegadores como o OpenSea pode renderizar nosso NFT da mesma forma como se fosse um link para um hash de armazenamento IPFS. No entanto, em vez de "obter e armazenar em cache" a imagem, o navegador renderiza a imagem para nós.

Como de costume, o código para conseguir isso pode ser encontrado e forked aqui; Remix será o modo mais fácil de testar o seu código**.

LFG.

Codificação Base64

Uma maneira de conseguirmos armazenamento de metadados on-chain e evitar a exigência de qualquer ferramenta como o IPFS é codificá-los com base64 e armazená-los diretamente em nossos dados de token NFT. Você pode estar familiarizado com a função TokenURI que retorna para nós e para o OpenSea, o hash para nossos metadados... bem, em nosso caso tokenURI retornará os metadados reais, em um formato codificado; isto não é mais um 'link', mas os metadados em si.

Como eu disse, estaremos alavancando uma biblioteca Base64.sol existente da GitHub. Você pode encontrar esse repositório aqui. Você pode importar do github ou simplesmente clonar/copiar o código e importar este arquivo do mesmo diretório em que você o colou.

Uma nota sobre codificação na Base64, a codificação não é uma forma de compressão de dados, pois não estamos reduzindo o tamanho de nossos dados, estamos simplesmente armazenando-os em um formato que nosso navegador possa decodificar. Nossos metadados NÃO são proibitivamente grandes, como é o caso de nossa imagem NFT. Você pode ver um exemplo disso abaixo:

Image description

Em nosso código de exemplo, aproveitamos a função 'BuildMetaData' que pega um tokenId (o id de nosso NFT) e retorna uma string de texto json codificada com base64 com tudo o que o OpenSea precisa para renderizar nosso NFT com seu Nome, Descrição, Atributos e muito importante, nossa Imagem. Ele também aproveita a função BuildImage que explicarei a seguir.

A seguir, um exemplo de nossos metadados:

{

  "name":"NFT1", 

  "description":"This is our on-chain NFT", 

  "image": "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAwIiBoZWlnaHQ9IjUwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCBpZD0ic3ZnXzExIiBoZWlnaHQ9IjYwMCIgd2lkdGg9IjUwMyIgeT0iMCIgeD0iMCIgZmlsbD0iaHNsKDI5Myw1MCUsMjUlKSIvPjx0ZXh0IGZvbnQtc2l6ZT0iMTgiIHk9IjEwJSIgeD0iNSUiIGZpbGw9ImhzbCg5MCwxMDAlLDgwJSkiPlNvbWUgVGV4dDwvdGV4dD48dGV4dCBmb250LXNpemU9IjE4IiB5PSIxNSUiIHg9IjUlIiBmaWxsPSJoc2woOTAsMTAwJSw4MCUpIj5Tb21lIFRleHQ8L3RleHQ+PHRleHQgZm9udC1zaXplPSIxOCIgeT0iMjAlIiB4PSI1JSIgZmlsbD0iaHNsKDkwLDEwMCUsODAlKSI+U29tZSBUZXh0PC90ZXh0Pjx0ZXh0IGZvbnQtc2l6ZT0iMTgiIHk9IjEwJSIgeD0iODAlIiBmaWxsPSJoc2woOTAsMTAwJSw4MCUpIj5Ub2tlbjogMTwvdGV4dD48dGV4dCBmb250LXNpemU9IjE4IiB5PSI1MCUiIHg9IjUwJSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iaHNsKDkwLDEwMCUsODAlKSI+dXNlclRleHQ8L3RleHQ+PC9zdmc+", 

  "attributes": [

    {

      "trait_type": "TextColor",

      "value":"328"

    }

  ]

}

Enter fullscreen mode Exit fullscreen mode

Normalmente, nosso valor de "imagem" seria algo parecido com isto:

{

"image": "ipfs://QmWwMDLz6hQKCqjYba5cSHdrNUvPvAdndtaWjdFpm52GYm/1.gif"

}
Enter fullscreen mode Exit fullscreen mode

Então, o que há com nosso valor da imagem? Bem, você está certo, nosso valor da Imagem é 1 - SVG e 2 - o SVG também é codificado na Base64; isto significa que nosso texto SVG foi codificado exatamente como nossos dados json (texto).

Você notará que acrescentamos algo ao nosso json codificado e que ele se parece com isto:

“data:application/json;base64,”

Isto simplesmente descreve o que são os dados e, portanto, como o destinatário ou o navegador do destinatário pode DECODIFICÁ-los adequadamente.

Codificação ABI

Você provavelmente notou que nós também realizamos a codificação ABI em todo o código de nosso projeto. A codificação ABI ou Interface Binária de Aplicação em nosso caso simplesmente nos permitirá concatenar múltiplas linhas de texto. "Isto", "é", "meu", "código" causará erros se não for codificado em uma única sequência.

SVG files

Então o que é SVG e por que isso é importante para nós? SVG ou gráficos vetoriais escaláveis basicamente nos permite armazenar imagens num tipo de formato xml ou como texto; texto que pode ser armazenado on-chain. Em vez de armazenar grandes dados de imagem, descrevemos nossa imagem desejada em texto e a codificamos de uma forma que nosso navegador e o OpenSea podem renderizar para nós. Podemos definir todos os tipos de características de nossa imagem, incluindo tamanho, cor e até mesmo texto que pode ser renderizado para nós.

Pense assim, se eu quiser enviar-lhe uma imagem simples, posso enviar-lhe ou por e-mail ou por SMS uma PNG de alta resolução, ou você poderia simplesmente descrevê-la em poucas palavras e deixar que seu destinatário a renderize ou gere para você. Se a data ou a transmissão de dados for cara, podemos fazer uma " negociação " reduzindo esse gasto através do aumento do custo, como esforço, por parte do destinatário.

A descrição do texto a seguir é provavelmente muito menos intensiva em termos de dados para retransmitir, do que uma PNG de alta definição. Desde que o destinatário tenha as ferramentas necessárias para renderizar isso facilmente, nós apenas reduzimos drasticamente o custo em termos de armazenamento ou transmissão de dados:

“tamanho 500x500, fundo azul com texto em branco dizendo ‘Hello World’”

Nosso código de exemplo descreve os parâmetros para nosso SVG na função BuildImage.

Existem muitas ferramentas e modelos on-line excelentes para a geração de SVG. Eu o incentivo a procurar algumas ferramentas que ajudem a tornar sua ideia uma realidade SVG.

Uma coisa que aconselharei é garantir que você use porcentagens para o layout, pois com o desenvolvimento de aplicativos os valores de 'codificação dura' podem nos causar problemas quando aumentamos ou reduzimos o tamanho da tela na qual fazemos a renderização. 1000 pixels para um ponto de partida de nosso texto pode ser bom, até reduzirmos o tamanho da tela de nosso dispositivo abaixo de 1000x1000, neste caso, seria melhor que tivéssemos ajustado isto para 80%.

Nosso SVG, antes da ABI e da codificação em Base64:

'<svg width="500" height="500" xmlns="http://www.w3.org/2000/svg">',

'<rect id="svg_11" height="600" width="503" y="0" x="0" fill="hsl(',

currentWord.bgHue,

',50%,25%)"/>',

'<text font-size="18" y="10%" x="5%" fill="hsl(',

random,

',100%,80%)">Some Text</text>',

'<text font-size="18" y="15%" x="5%" fill="hsl(',

random,

',100%,80%)">Some Text</text>',

'<text font-size="18" y="20%" x="5%" fill="hsl(',

random,

',100%,80%)">Some Text</text>',

'<text font-size="18" y="10%" x="80%" fill="hsl(',

random,

',100%,80%)">Token: ',

_tokenId.toString(),

"</text>",

'<text font-size="18" y="50%" x="50%" text-anchor="middle" fill="hsl(',

random,

',100%,80%)">',

currentWord.value,

"</text>",

"</svg>"
Enter fullscreen mode Exit fullscreen mode

O resultado de nossa BuildImage com SVG pode ser visto aqui:

Image description

Você notará mais uma vez que anexamos detalhes com relação aos dados:

“data:image/svg+xml;base64”

Input do Usuário

Uma das características mais interessantes do nosso contrato inteligente é a capacidade do usuário de contribuir para o NFT final, inserindo alguns dados de texto na função mint. Este input do usuário é salvo como uma _'string _de memória' e posteriormente adicionado aos nossos dados SVG dinamicamente através da função BuildImage.

Limitei o tamanho do input do texto e acrescentei um erro para esta restrição, mas do contrário, o usuário tem uma gama completa do que poderia acrescentar. Isto é imutável e viverá para sempre na blockchain - isto pode fazer com que alguns queiram restringir certas palavras, mas de acordo com a natureza democratizada da tecnologia blockchain, eu não coloquei mais limitações ou censura.

Nossa função mint alcança este recurso simplesmente adicionando uma expectativa de string na função:


function mint(string memory _userText) public payable {

    uint256 supply = totalSupply();

    bytes memory strBytes = bytes(_userText);

    require(strBytes.length <= stringLimit, "String input exceeds limit.");

    require(exists(_userText) != true, "String already exists!");



    Word memory newWord = Word(

        string(

            abi.encodePacked(

                "NFT",

                uint256(supply + 1).toString()

            )

        ),

        "This is our on-chain NFT",

        randomNum(361, block.difficulty, supply).toString(),

        randomNum(361, block.timestamp, supply).toString(),

        _userText

    );

    if (msg.sender != owner()) {

        require(msg.value >= 0.005 ether);

    }

    wordsToTokenId[supply + 1] = newWord; //Add word to mapping @tokenId

    _safeMint(msg.sender, supply + 1);

}
Enter fullscreen mode Exit fullscreen mode

Interagindo com nosso NFT & Contrato Inteligente

Se você estiver usando uma ferramenta como o Remix, você pode simplesmente alterar o código fornecido, carregá-lo no Remix, compilá-lo e implantá-lo para testar.

Uma vez que nossa função mint espera dados de string do input do usuário, você poderá adicionar texto e depois utilizar nossa função tokenURI para ver o que é produzido, esta é a mesma função tokenURI que os mercados como o OpenSea irão utilizar para recuperar OU resolver nossos dados e imagem do NFT.

Image description

Então, o que fazemos com isso? Para renderizar isto em seu navegador, você vai querer copiar tudo APÓS o 'string' (não precisamos disto) e colar isto em nosso navegador. O resultado de colar isto em nosso navegador será o seguinte:

Image description

Detalhes dos metadados de nossa função TokenURI, colados em nosso navegador local

Além disso, podemos ver nossa imagem, copiando o valor "imagem". A parte que queremos copiar está destacada aqui:

Image description

E o resultado pode ser visto aqui:

Image description

Dados de imagem colados em nosso navegador local

Nosso NFT

Com isso, criamos um contrato inteligente que permitirá aos usuários inserir texto no momento da cunhagem, gerar dados SVG que incluem nosso input de usuário, codificá-los na Base64 e adicioná-los a nossos metadados que também são codificados na Base64. O resultado, um NFT on-chain que renderizará em mercados como o OpenSea tal como:

Image description

Imagem do nosso NFT

**Uma nota sobre o Remix

Se você optar por testar seu código no Remix, poderá se deparar com um erro de timeout quando tentar executar o tokenURI - isto se deve às limitações da JVM online e pode ser resolvido simplesmente trocando sua configuração de 'Ambiente' para 'Injected web3'. Eu uso pessoalmente a rede de teste Rinkeby com minha carteira Metamask. Você precisará da testnet Rinkeby Eth para o gas, da Rinkeby faucet.

Image description

Rinkeby é uma ótima maneira de testar e a cunhagem bem sucedida significa que você pode ver seus NFTs na instância da Rinkeby OpenSea, essencialmente um clone do OpenSea.

Feliz teste!

Vídeo

Para um percurso extremamente detalhado de código muito semelhante, embora sem a funcionalidade de entrada do usuário na cunhagem, verifique o seguinte vídeo do Youtube e siga o Canal do Hashlips onde você encontrará todos os tipos de excelente conteúdo sobre NFT.

Este artigo foi escrito por Parker Ferguson e traduzido por Fátima Lima. Seu original pode ser lido aqui.

Latest comments (0)