WEB3DEV

Cover image for Análise de Gás de Metadados NFT on-chain
Dimitris Carvalho Calixto
Dimitris Carvalho Calixto

Posted on

Análise de Gás de Metadados NFT on-chain

Há muitas opções disponíveis para escolher armazenar os metadados dos NFTs. Mas podemos categorizar todas essas opções em categorias on-chain e off-chain. Neste artigo, primeiramente passamos pelas diferenças destas duas opções e depois analisamos o uso de gás de cunhagem e múltiplos NFTs usando cada tipo.

Image

Dados on-chain

Não há nada de especial nisto. É apenas o que chamamos de dados publicamente disponíveis que são armazenados na blockchain. O que há de especial neste tipo de dados é que, como estão armazenados na blockchain, todos nós temos acesso aos mesmos dados seguros e precisos. A verificação é outra vantagem importante do armazenamento de dados na blockchain. Quando escrevemos um valor na blockchain, estamos realmente fazendo uma transação e cada transação precisa ser validada. Além disso, sabemos que quando uma transação é confirmada, não há maneira de modificá-la ou revertê-la.

O único problema com os dados on-chain é que eles são caros.

Dados off-chain

Por off-chain, entendemos tudo o que não é salvo na blockchain. Pode ser seu computador ou um servidor. Embora haja muitas ferramentas de segurança disponíveis para muitos casos de uso, se escolhermos armazenar os dados usando este método, perderemos todas as vantagens do armazenamento on-chain.

Armazenamentos distribuídos como o IPFS são uma alternativa ao armazenamento em blockchain, uma vez que utiliza um mecanismo distribuído.

A maior vantagem de usar o armazenamento off-chain, por exemplo, um servidor, é que você poderá personalizá-lo de acordo com seu uso. Você pode executar uma lógica complicada no servidor e não se preocupar com o custo do gás. Além disso, como não há necessidade de validação e distribuição, salvar e acessar este tipo de dados é muito mais rápido do que o tipo on-chain.

Image

O contrato inteligente

A fim de realmente ver a diferença de uso de gás na cunhagem de NFTs usando dados on-chain e off-chain, escreveremos um simples contrato NFT e implementaremos duas funções de cunhagem no mesmo.

Primeiro, precisamos modificar o arquivo de configuração do hardhat de forma a dizer ao hardhat para mostrar o uso do gás quando fizermos testes. Para fazer isso, basta adicionar o objeto gasReporter.

gasReporter: {

 enabled: true,

 currency: “USD”

}

Enter fullscreen mode Exit fullscreen mode

Então adicionaremos a lógica de armazenamento on-chain ao contrato inteligente NFT. No contrato inteligente a seguir, você pode ver que existem duas funções mint (mintar, cunhar). Um para a cunhagem off-chain e outro para a cunhagem on-chain. A única diferença é que a função de cunhagem on-chain usa o ERC721URIStorage para armazenar o URI de cada NFT de forma independente. Para os NFTs off-chain, podemos armazenar os metadados da coleção no IPFS e somente atualizar o URI de base.

Finalmente, precisamos escrever testes para medir o uso de gás de cada método. Eu escrevi dois testes para cada método de cunhagem. Um para uma única cunhagem NFT e outro para várias.


require("@nomicfoundation/hardhat-toolbox");

/** @type import('hardhat/config').HardhatUserConfig */

module.exports = {

  solidity: "0.8.17",

  gasReporter: {

    enabled: true,

    currency: "USD"

  },

};

Enter fullscreen mode Exit fullscreen mode

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

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

import "@openzeppelin/contracts/access/Ownable.sol";

import "@openzeppelin/contracts/utils/Counters.sol";

import "@openzeppelin/contracts/utils/Base64.sol";

contract NFT is ERC721, ERC721URIStorage, Ownable {

    using Counters for Counters.Counter;

    Counters.Counter private _tokenIdCounter;

    constructor() ERC721("NFT", "NFT") {}

    function makeURI(uint256 tokenId)

        public

        pure

        returns (string memory)

    {

        return

            string(

                abi.encodePacked(

                    "data:application/json;base64,",

                    Base64.encode(

                        bytes(

                            abi.encodePacked('{"name": "NFT", "description": "Simple on-chain metadata nft", "tokenId": "', tokenId, '"}')

                        )

                    )

                )

            );

    }

    function mintOffChain(uint256 count) public onlyOwner {

        for (uint256 i = 0; i < count; ++i) {

            uint256 tokenId = _tokenIdCounter.current();

            _tokenIdCounter.increment();

            _safeMint(msg.sender, tokenId);

        }

    }

    function mintOnChain(uint256 count) public onlyOwner {

        for (uint256 i = 0; i < count; ++i) {

            uint256 tokenId = _tokenIdCounter.current();

            _tokenIdCounter.increment();

            _safeMint(msg.sender, tokenId);

            _setTokenURI(tokenId, makeURI(tokenId));

        }

    }

    // As seguintes funções são sobreposições exigidas pelo Solidity.

    function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {

        super._burn(tokenId);

    }

    function tokenURI(uint256 tokenId)

        public

        view

        override(ERC721, ERC721URIStorage)

        returns (string memory)

    {

        return super.tokenURI(tokenId);

    }

}

Enter fullscreen mode Exit fullscreen mode

const { ethers } = require("hardhat");

const { expect } = require("chai");

const { loadFixture } = require("@nomicfoundation/hardhat-network-helpers");

describe("NFT", function () {

    async function deploy() {

        const NFT = await ethers.getContractFactory("NFT");

        const nft = await NFT.deploy();

        return { nft };

    }

    it("Mint off-chain single", async function () {

        const { nft } = await loadFixture(deploy);

        await (await nft.mintOffChain(1)).wait();

    });

    it("Mint on-chain single", async function () {

        const { nft } = await loadFixture(deploy);

        await (await nft.mintOnChain(1)).wait();

    });

    it("Mint off-chain multiple", async function () {

        const { nft } = await loadFixture(deploy);

        await (await nft.mintOffChain(10)).wait();

    });

    it("Mint on-chain multiple", async function () {

        const { nft } = await loadFixture(deploy);

        await (await nft.mintOnChain(10)).wait();

    });

});

Enter fullscreen mode Exit fullscreen mode

Os resultados

Escrevendo os contratos inteligentes, alguns testes e configurando o harddhat, agora somos capazes de executar os testes e ver os resultados do uso do gás.

Image

Você pode ver que o uso de gás pelo método on-chain é muito maior do que o método off-chain.

Encerramento

O objetivo deste artigo não é encorajá-lo a parar de usar metadados on-chain em seus contratos. Mas a questão é que você só precisa usá-los se for necessário!

O processo de tomada de decisão não é fácil e muitos fatores estão envolvidos. Mas você precisa rever os prós e os contras de usar cada método de armazenamento e escolher um ou uma mistura deles.

Se você deseja construir sua própria coleção NFT, a Bitium Agency, como uma empresa pioneira no desenvolvimento de blockchain, pode ajudá-lo a implementar sua própria coleção NFT com os melhores especialistas proficientes.

Artigo escrito por Parham Alimardanian. A versão original pode ser encontrada aqui. Traduzido e adaptado por Dimitris Calixto.

Top comments (0)