WEB3DEV

Cover image for Criando um Mercado de NFT Com Solidity Com apenas 200 linhas de código
Fatima Lima
Fatima Lima

Posted on • Atualizado em

Criando um Mercado de NFT Com Solidity Com apenas 200 linhas de código

Image description

Web 3.0 tem o potencial de mudar para sempre a internet que conhecemos.

— Adrian ( JavaScript Mastery )

Neste artigo, eu explicarei Solidity e como se pode criar um mercado de NFT com apenas 200 linhas de código.

Este, no entanto, é um contrato inteligente. Um contrato inteligente poderia ser definido como uma entidade que pode enviar e receber moeda (cripto) além de ser apenas usuários na web. É um código que gerencia seu dinheiro e como você o direciona para executar certas funções - logo, programas na blockchain!

Estes programas, uma vez implantados, não podem ser modificados e são finais. Não vamos nos aprofundar nos muitos aspectos benéficos dos contratos inteligentes, pois são muitos e o tempo é relativo.


Sumário

1 . Começando com a demo da aplicação

2 . Código Solidity

3 . Links


Começando com a demo da aplicação

  1. Conecte-se com nossa carteira metamask ( você pode continuar lendo esse artigo mesmo que você não tenha uma conta metamask ou não tenha ideia do que seja isso :|, apenas assuma que é uma carteira online )

Image description

Conectado com a carteira metamask

 

  1. Crie uma NFT

Image description

Crie uma página NFT (zoom 50%, para caber aqui)

 
Carregue sua imagem e preencha os detalhes.

Image description

Confirmação da Metamask para listar NFT no mercado.

 
Você enviará 0.00025 ether ao proprietário do mercado como taxa de listagem. A imagem acima é a aprovação da transação. Pense nela como digitando seu código PIN ou OTP para uma transação on-line.

Image description

Sua imagem agora está no mercado, voila!

 

  1. Vamos comprar essa NFT que você listou. Agora vamos mudar nossa conta na metamask, porque vamos ser sinceros aqui, não podemos comprar nosso próprio NFT.

Image description

Trocando a conta na metamask.

 

  1. Compre o NFT.

Image description

Página de detalhes do NFT.

 

Image description

Confirmação do *checkout*

 
Image description

Confirmação da compra do NFT.

 
Image description

Agora você pode verificar que comprou a NFT na página de detalhes da NFT.

 
Agora que a demonstração terminou, podemos começar com o código Solidity.
 

Código Solidity

Para iniciantes, se você sabe JavaScript, então você se sentirá confortável em compreender o código de Solidity. Contratos inteligentes para a Ethereum são programados em Solidity, que é uma linguagem de alto nível, orientada ao objeto.

Image description

Iniciando o código.

 
Vamos passar por este código linha por linha:

Linha 2 — É obrigatório mencionar a versão de Solidity antes de escrever nosso contrato. Aqui estaremos trabalhando com a versão 0.8.4.

Linhas 4,5,6 — Como qualquer outra linguagem, aqui estamos importando outros contratos que estamos usando para construir sobre nosso código, pense neles como bibliotecas.

Counters.sol: Para manter um contador de - ( será discutido mais adiante ).

ERC721.sol: Padrão para escrever contratos relevantes para as NFTs.

ERC721URIStorage.sol: Para herdar _tokenURI, deve deliberar um documento JSON

Linha 8 — Definindo um contrato NFTMarketplace que herda ERC721URIStorage ( outro contrato ), nós não herdamos ERC721.sol já que ERC721URIStorage já a herda. Esses contratos são feitos por OpenZepplin.

using Counters for Counters.Counter;
Enter fullscreen mode Exit fullscreen mode

Inicializando os Counters que herdamos.

Counters.Counter private _tokenIds;
Counters.Counter private _itemsSold;
Enter fullscreen mode Exit fullscreen mode

Aqui o contrato mantém a contagem de duas variáveis — _tokenIds e _itemsSold. Sempre que chamamos _tokenIds.increment(); o valor dos _tokenIds será incrementado de, digamos, 1 → 2, da mesma forma para _itemsSold.

uint256 listingPrice = 0.00025 ether;
Enter fullscreen mode Exit fullscreen mode

Especificando o preço de listagem, sempre que alguém quiser criar seu NFT e listá-lo no mercado, pagará a taxa de listagem, ou seja, 0,00025 ETH. Aqui uint256é um tipo de dado. u → não assinado, int → número inteiro com base 256.

address payable owner;
Enter fullscreen mode Exit fullscreen mode

A variável do tipo endereço (só recebe endereços) chamou o proprietário. Aqui é definida como payable, significando que esse endereço pode envolver transações.

constructor() ERC721("Metaverse Tokens", "METT") {
  owner = payable(msg.sender);
}
Enter fullscreen mode Exit fullscreen mode

Sempre que nosso contrato é chamado, um constructor é invocado. Em nosso caso, precisamos invocar 2 constructors - nossos contratos e o contrato ERC721.sol, já que estamos herdando dele.

Neste constructor, estamos dizendo ao contrato que o proprietário é a pessoa que está chamando o contrato, mas na blockchain, transmitimos isto por endereço, obtendo assim o endereço do remetente. msg.sender é um argumento incorporado que recebe o endereço da conta que está invocando/chamando o contrato.

Resumindo, estamos informando ao contrato que nós ( msg.sender) somos o proprietário, definindo a variável de proprietário! Quando implantamos nosso contrato, ele recebe nosso endereço, de modo que somente nós temos a autoridade de propriedade.

ufa...

Image description

Durante a chamada do *constructor* do ERC721, precisamos chamá-lo como ele quer ser chamado, ou seja, com seus parâmetros necessários.

 

struct MarketItem {
  uint256 tokenId;
  address payable seller;
  address payable owner;
  uint256 price;
  bool sold;
}
Enter fullscreen mode Exit fullscreen mode

Criando uma estrutura Market Item para armazenar vários tipos de dados.

  • tokenID → Id do token envolvido na venda
  • seller → para armazenar o endereço do vendedor
  • owner → para armazenar o endereço do proprietário (Se um NFT é criado, inicialmente ele pertence ao mercado, ou seja, endereço do Contrato)
  • price → preço do NFT
  • sold → valor booleano (verdadeiro/falso ) para mencionar se o NFT está vendido ou não.
mapping(uint256 => MarketItem) private idToMarketItem;
Enter fullscreen mode Exit fullscreen mode

mapping — outro tipo de dados da Solidity. Ele armazena uma chave (um tipo de dados) e seu valor correspondente também um tipo de dados), portanto — Key — pares de valores.

mapping ( <data-type> => <data-type/structure>/ ) visibility name;
Enter fullscreen mode Exit fullscreen mode

Nesse caso, estamos mapeando um tipo de dados uint256para o struct MarketItem. Aqui, a visibilidade é privada, ou seja, ela só pode ser chamada de dentro do contrato e não de fora.

function updateListingPrice(uint _listingPrice) public payable {
    require(owner == msg.sender);
    listingPrice = _listingPrice;
}
function getListingPrice() public view returns (uint256) {
    return listingPrice;
}
Enter fullscreen mode Exit fullscreen mode

Muito bem, vamos melhorar o ritmo agora.

  • updateListingPrice() — Recebe um uint → _listingPrice como um argumento e atualiza o preço atual da listagem. getListingPrice() — Uma função de retorno que retorna o preço atual da listagem do contrato.

Aqui, require é uma condição que é executada antes que o código sob ela seja executado. Portanto, em teoria, se alguém além do proprietário chama as funções acima, simplesmente não funciona.

Fazendo um desvio agora...

Você deve se perguntar o que são privados, públicos, etc.

  • público, privado, interno, externo → Estes são apenas tipos de visibilidade que são dados a cada função/variável informando ao contrato quem pode chamar estas funções/variáveis e de onde.
  • public→ Pode ser chamada por qualquer pessoa.
  • private→ Pode ser chamado somente de dentro do contrato e não por qualquer outra pessoa fora dele.
  • external→ Pode ser chamado por contratos que herdam a função com visibilidade externa.
  • internal (default )→ Só podem ser acessíveis dentro do contrato em que foram declarados e também a partir de contratos derivados.

Prosseguindo com o contrato…

function createToken(string memory tokenURI, uint256 price) public                                                                                                                                       payable returns (uint) {
  _tokenIds.increment();
  uint256 newTokenId = _tokenIds.current();
  _mint(msg.sender, newTokenId);
  _setTokenURI(newTokenId, tokenURI);
  createMarketItem(newTokenId, price);
  return newTokenId;
}
Enter fullscreen mode Exit fullscreen mode

Como essa função é chamada em JavaScript:

createToken(url, price, { value: listingPrice.toString() })

Enter fullscreen mode Exit fullscreen mode
  • _tokenIds.increment() — aumenta o valor de _tokenIds no contrato por 1.
  • uint256 newTokenId = _tokenIds.current() — armazena o valor atual de _tokenIds do contrato para a variável newTokenId.
  • _mint(msg.sender, newTokenId)Esta função _mint() é herdada do contrato ERC721.sol. É usada para cunhar um novo token para o contrato e na blockchain.

Image description

_mint funtion in ERC721.sol

 
_setTokenURI(newTokenId, tokenURI) — Esta função _setTokenURI() também é herdada do contrato ERC721URIStorage.sol. É usada para definir um token URI e verificar se o token já existe.

Image description

função _setTokenURI do contrato ERC721URIStorage.sol

 
createMarketItem(newTokenId, price) — Esta é a função dentro do contrato, que será explicada a seguir.

return newTokenId— retorna o ID do novo token.

Image description

Visão geral de como um NFT é cunhado.

 
Finalmente, esta função transfere 0,00025 ETH - (taxa de listagem) para o contrato como preço de listagem.

Image description

Captura de tela do goerli.etherscan.io

 

function createMarketItem(uint256 tokenId, uint256 price) private {
require(price > 0, "Price must be at least 1 wei");
require(msg.value == listingPrice);
idToMarketItem[tokenId] = MarketItem(tokenId,payable(msg.sender),payable(address(this)),price,false);
_transfer(msg.sender, address(this), tokenId);
emit  MarketItemCreated( tokenId , msg.sender, address(this),price,false);
}
Enter fullscreen mode Exit fullscreen mode

A função acima é chamada pela função createToken. Uma vez que um token é criado, ele precisa estar no Mercado, para isso chamamos esta função. Observe que mencionamos aqui a visibilidade como privada, o que diz ao contrato que ele só pode ser chamado de dentro do contrato.

As duas primeiras linhas verificam que o preço do token a ser listado no mercado deve ser maior que 0 e que o valor enviado junto com a função deve ser exatamente igual à taxa de listagem mencionada, ou seja, 0,00025 ether.

Próxima linha idToMarketItem[tokenId] = MarketItem (tokenId, payable(msg.sender) ,payable(address(this)),price,false). Como mencionado acima, idToMarketItem é um mapeamento que mapeia a estrutura MarketItem para uma chave específica, aqui, o tokenId. Portanto, para um ID específico do token, temos a estrutura que ele aponta. Podemos passar os parâmetros da estrutura como mencionado acima.

payable(msg.sender)— endereço da carteira que chama o contrato.

payable(address(this)) — endereço do contrato.

price— o preço do NFT.

false— referente ao bool sold.

_transfer(msg.sender, address(this), tokenId);— Esta também é uma função que foi herdada do contrato ERC721.sol. É usada para mencionar a transferência de token de um endereço para outro. Aqui nesse caso, é a transferência de msg.senderaddress(this).

emit MarketItemCreated(tokenId,msg.sender,address(this),price,false); — Isto emite um evento. Os eventos são como registros no contrato que notificam se algo aconteceu. Para isso, é preciso definir o evento MarketItemCreated em nosso código.

event MarketItemCreated (
    uint256 indexed tokenId,
    address seller,
    address owner,
    uint256 price,
    bool sold
);
Enter fullscreen mode Exit fullscreen mode

Image description

Registros de eventos MarketItemCreated no goerli.etherscan.io

 

function resellToken(uint256 tokenId, uint256 price) public payable {
    require(idToMarketItem[tokenId].owner == msg.sender);
    require(msg.value == listingPrice);
    idToMarketItem[tokenId].sold = false;
    idToMarketItem[tokenId].price = price;
    idToMarketItem[tokenId].seller = payable(msg.sender);
    idToMarketItem[tokenId].owner = payable(address(this));
     _itemsSold.decrement();
     _transfer(msg.sender, address(this), tokenId);
}
Enter fullscreen mode Exit fullscreen mode

Praticamente semelhante à função createToken, esta função já possui o URL IPFS que especifica os dados. Portanto, em nosso código, apenas mudamos o proprietário de nosso endereço para o endereço do contrato. Além disso, também mencionamos o novo preço da NTF juntamente com a mudança do boolvendido para false.

Penúltimo, diminuímos a variável _itemsSold de 1. Por último, chamamos a função _transferdo contrato ERC721.sol que herdamos para transferir a propriedade.

function createMarketSale(uint256 tokenId) public payable {
  uint price = idToMarketItem[tokenId].price;
  address payable creator = idToMarketItem[tokenId].seller;
  require(msg.value == price);
  idToMarketItem[tokenId].owner = payable(msg.sender);
  idToMarketItem[tokenId].sold = true;
  idToMarketItem[tokenId].seller = payable(address(0));
  _itemsSold.increment();
  _transfer(address(this), msg.sender, tokenId);
  payable(owner).transfer(listingPrice);
  payable(creator).transfer(msg.value);
}
Enter fullscreen mode Exit fullscreen mode

Esta função trata da transferência de dinheiro (ETH) de um endereço para outro. Isto cria uma venda no mercado - o que acontece quando alguém está comprando um NFT.

.transfer() — função embutida para transferir cripto.

function fetchMarketItems() public view returns (MarketItem[] memory) {
uint itemCount = _tokenIds.current();
uint unsoldItemCount = _tokenIds.current() - _itemsSold.current();
uint currentIndex = 0;
MarketItem[] memory items = new MarketItem[](unsoldItemCount);
for (uint i = 0; i < itemCount; i++)
   {
     if (idToMarketItem[i + 1].owner == address(this))
      {
        uint currentId = i + 1;
        MarketItem storage currentItem = idToMarketItem[currentId];
        items[currentIndex] = currentItem;
         currentIndex += 1;
      }
   }
return items;
}
Enter fullscreen mode Exit fullscreen mode

Nesta função de retorno, buscamos os NFTs que são atualmente propriedade do contrato. — idToMarketItem[i + 1].owner == address(this).

Para isso, nós primeiro criamos um _array _(vetor) temporário MarketItemchamado items e damos a ele um tamanho fixo unsoldItemCount, o qual nós obtemos subtraindo o índice atual de _tokenIds pelo número atual de itens vendidos em nosso contrato— _itemsSold.current().

Depois, iteramos por meio desse vetor e aplicamos a condição para verificar se o proprietário do NFT é o contrato ou não. Se for, então passamos os detalhes do NFT para o vetor temporário criado (items). No final, retorna o vetor items.

Image description

Itens do Mercado

 

function fetchMyNFTs() public view returns (MarketItem[] memory) {
uint totalItemCount = _tokenIds.current();
uint itemCount = 0;
uint currentIndex = 0;
for (uint i = 0; i < totalItemCount; i++) {
   // verifique se a NFT é minha
   if (idToMarketItem[i + 1].owner == msg.sender)
   {
    itemCount += 1;
   }
 }
MarketItem[] memory items = new MarketItem[](itemCount);
for (uint i = 0; i < totalItemCount; i++) {
    if (idToMarketItem[i + 1].owner == msg.sender)
    {
     uint currentId = i + 1;
     MarketItem storage currentItem = idToMarketItem[currentId];
     items[currentIndex] = currentItem;
     currentIndex += 1;
     }
   }
return items;
}
Enter fullscreen mode Exit fullscreen mode

Similar à função acima, aqui acrescentamos um passo adicional, ou seja, para encontrar o número de NFTs que possuímos, fazemos um loop através da lista de todas as NFTs.

Isso é feito para fornecer o vetor temporário items, seu tamanho — itemCount.

Image description

Meus NFTs

 

function fetchItemsListed() public view returns (MarketItem[] memory) {
uint totalItemCount = _tokenIds.current();
uint itemCount = 0;
uint currentIndex = 0;
for (uint i = 0; i < totalItemCount; i++) {
   if (idToMarketItem[i + 1].seller == msg.sender) {
     itemCount += 1;
   }
 }
MarketItem[] memory items = new MarketItem[](itemCount);
for (uint i = 0; i < totalItemCount; i++) {
   if (idToMarketItem[i + 1].seller == msg.sender) {
     uint currentId = i + 1;
     MarketItem storage currentItem = idToMarketItem[currentId];
     items[currentIndex] = currentItem;
     currentIndex += 1;
    }
  }
return items;
}
Enter fullscreen mode Exit fullscreen mode

Aqui também, semelhante à função acima, obtemos a lista de NFTs que estamos vendendo, idToMarketItem[i + 1].seller == msg.sender .

Image description

NFTs listadas.

 
Aqui está o código Solidity completo.

https://gist.github.com/Khanisic/ac54144abd4eea043ff31953a98ab29f
Enter fullscreen mode Exit fullscreen mode

Links



Agradecimentos especiais ao Adrian da Javascript Mastery  que me ensinou esse projeto em seu curso NFT Marketplace.

Este artigo foi escrito por [Moid Khan](https://skhan852000.medium.com/?source=post_page-----2323abca6346--------------------------------), traduzido por Fátima Lima e seu original pode ser lido [aqui.](https://betterprogramming.pub/creating-an-nft-marketplace-solidity-2323abca6346)

16 de julho de 2022
Enter fullscreen mode Exit fullscreen mode

Top comments (0)