WEB3DEV

Cover image for Como criar um mercado de NFTs com Next.js e Thirdweb na rede Polygon
Arnaldo Pereira Campos Junior
Arnaldo Pereira Campos Junior

Posted on

Como criar um mercado de NFTs com Next.js e Thirdweb na rede Polygon

alt_text

Você provavelmente já ouviu falar de NFTs antes, mas apenas no caso de não ter ouvido, tokens não-fungíveis (NFTs) são identificadores digitais únicos que não podem ser copiados, substituídos ou subdivididos. Eles são registrados em uma blockchain para certificar autenticidade e propriedade.

À medida que a popularidade dos NFTs cresceu, o mercado de NFTs alcançou um valor superior a US$40 bilhões. E atualmente, a melhor maneira de comprar e vender NFTs é por meio de mercados de NFTs.

Mercados de NFTs como o OpenSea, Axie Marketplace, NBA Top Shot, Rarible, Nifty Gateway, Sudoswap entre outros, são todos líderes nesse espaço. Eles atuam como sua porta de entrada para participar da compra e venda de ativos digitais, desde arte até música e até mundos virtuais inteiros.

Na Lazer, trabalhamos com muitos projetos de mercado de NFTs, incluindo Sudoswap, Stardust, Genies, NFL All Day, e pensamos que seria ótimo fazer um tutorial sobre como construir uma versão simplificada de um mercado de NFTs.

Neste tutorial, construiremos um mercado de NFTs na rede de teste Polygon Mumbai usando a estrutura Next.js com Typescript e o SDK Thirdweb.

O resultado final será semelhante às imagens abaixo:

alt_text

alt_text

alt_text

Depois de construir o mercado de NFTs, ele deve ser capaz de:

  • Ver todos os NFTs disponíveis para venda
  • Visualizar NFTs individuais e seus detalhes
  • Permitir que os usuários comprem os NFTs
  • (Bônus) Ver todos os NFTs em uma carteira

Pré-requisitos necessários para realizar isso:

  • Uma carteira digital (por exemplo, Metamask)
  • A carteira está conectada a rede de teste Polygon Mumbai, com mais de 1 MATIC na carteira. Se não estiver, vá para a torneira Mumbai da Alchemy's para solicitar 1 MATIC https://mumbaifaucet.com/
  • Conhecimento básico de Nextjs e Typescript
  • Conhecimento básico de NFTs e Contratos Inteligentes

Passo 1: Crie e cunhe seus NFTs na Thirdweb

Antes de podermos criar o mercado de NFTs, primeiro precisamos criar um contrato de coleção de NFTs e cunharr alguns NFTs. O contrato de coleção de NFTs é adequado quando você deseja ter uma coleção de NFTs exclusivos, mas não "lançá-los" ou "liberá-los" para sua comunidade reivindicar.

  1. Vá para o painel da Thirdweb.
  2. Crie um novo contrato de coleção de NFTs. Clique em Deploy new contract → NFT Collection → Deploy now. Mantenha todas as configurações iguais, selecione a rede Mumbai (MATIC).
  3. Acesse o contrato de coleção de NFTs que você acabou de implantar e cunhealguns NFTs diferentes. Por exemplo:

alt_text

Passo 2: Crie e implante um contrato de mercado de NFTs na Thirdweb

Agora que você cunhou alguns NFTs, vamos criar e implantar um contrato de mercado de NFTs. Um contrato de mercado de NFTs é um contrato em que você pode comprar e vender NFTs, como o OpenSea ou o Rarible. O contrato do mercado permite que os usuários listem NFTs para venda direta ou leilão. Outros usuários podem fazer ofertas ou comprar os NFTs pelo valor especificado na lista. O mercado pode ser configurado para permitir apenas que determinados usuários listem NFTs para venda, ou permitir que qualquer usuário liste NFTs para venda.

  1. Vá para o painel da Thirdweb.
  2. Crie um novo contrato de mercado de NFTs. Deploy new contract → NFT Marketplace → Deploy now. Mantenha todas as configurações iguais, selecione a rede Mumbai (MATIC).
  3. Vá para o contrato de mercado que você acabou de implantar e crie listas. Para este tutorial, selecione o tipo de listagem Direta.

alt_text

Passo 3: Vamos escrever algum código Typescript!

Primeiro, vamos clonar um repositório do GitHub que já tem o estilo pronto para você.

Isenção de responsabilidade: Este não é um tutorial de CSS/HTML, então vamos começar clonando este repositório do GitHub que já possui estilo: https://github.com/LazerTechnologies/nft-marketplace-tutorial

Depois de clonado, execute:

Se você estiver preso, mude para a filial principal para ver o produto final.

git checkout blank
npm install
npm run dev
Enter fullscreen mode Exit fullscreen mode

Configurando os SDKs da Thirdweb em nosso aplicativo

Dentro do arquivo _app.tsx, envolva tudo com ThirdwebProvider como abaixo, com o ID da cadeia sendo Mumbai.

const MyApp: AppType = ({ Component, pageProps }) => {
 return (
  <ThirdwebProvider desiredChainId={ChainId.Mumbai}>
   <Component {...pageProps} />
  </ThirdwebProvider>
 );
};
Enter fullscreen mode Exit fullscreen mode

Configurar a autenticação Web3 usando a Thirdweb

Crie um arquivo chamado components/AuthProvider.tsx

import { ReactElement, useEffect, useState } from "react";
import { ConnectWallet, useAddress } from "@thirdweb-dev/react";

export default function AuthProvider({
 children,
}: {
 children: React.ReactNode;
}): ReactElement {
 const [isLoggedin, setIsLoggedin] = useState(false);
 const address = useAddress();

 useEffect(() => {
  if (address) {
   setIsLoggedin(true);
  } else {
   setIsLoggedin(false);
  }
 }, [address]);

 if (!isLoggedin) {
  return (
   <div className={"flex h-screen w-full items-center justify-center"}>
    <div>
     <h1 className={"text-lg"}>Por favor, faça o login para continuar...</h1>
     <ConnectWallet />
    </div>
   </div>
  );
 }
 return <>{children}</>;
}
Enter fullscreen mode Exit fullscreen mode

Também envolva o <Component> em _app.tsx com AuthProvider.

const MyApp: AppType = ({ Component, pageProps }) => {
 return (
  <ThirdwebProvider desiredChainId={ChainId.Mumbai}>
   <AuthProvider>
    <Component {...pageProps} />
   </AuthProvider>
  </ThirdwebProvider>
 );
};
Enter fullscreen mode Exit fullscreen mode

Agora o usuário verá isso quando não estiver conectado.

alt_text

Obtendo os NFTs listados em seu contrato de Mercado

No arquivo index.tsx, obtenha o contrato de mercado usando useContract, e obtenha as listagens ativas usando useActiveListings.

import type { NextPage } from "next";
import { useActiveListings, useContract } from "@thirdweb-dev/react";

const Home: NextPage = () => {
 const { contract } = useContract(
  "ENDEREÇO DO CONTRATO DO SEU MERCADO>",
  "MERCADO"
 );

 const { data, isLoading } = useActiveListings(contract);

 console.log(data);

 return (
  <div>NFT Marketplace</div>
 );
};

export default Home;
Enter fullscreen mode Exit fullscreen mode

Abra o console da web e você deverá ver os dados do NFT 😊

alt_text

Agora vamos criar um componente para mostrar cada um desses NFTs.

Vamos criar o arquivo components/NFTCard.tsx e adicionar o seguinte:

import Image from "next/image";
import { MediaRenderer } from "@thirdweb-dev/react";

export default function NFTCard({
 nft,
}: {
 nft: {
  tokenUri: string;
  name: string;
  price?: string;
 };
}) {
 return (
  <div
   className={`relative flex cursor-pointer
   flex-col overflow-hidden rounded-lg bg-white shadow-lg
   transition-all duration-300 hover:shadow-2xl dark:bg-[#333333]`}
  >
   <MediaRenderer
    src={nft.tokenUri}
    style={{
     objectFit: "cover",
    }}
    className={
     "h-[244px] rounded-lg transition duration-300 ease-in-out hover:scale-105"
    }
   />
<div className={`flex flex-col gap-y-3 p-3`}>
 <div className={`text-sm font-semibold`}>{nft.name}</div>

 {nft.price && (
  <div>
   <div className={`text-xs font-semibold`}>Price</div>
   <div className={`flex items-center gap-x-1`}>
    <Image src={"/matic-logo.png"} height={16} width={16} />
    <p className={`text-base font-semibold`}>{nft.price}</p>
   </div>
  </div>
 )}
</div>
</div>
);
}
Enter fullscreen mode Exit fullscreen mode

Vamos exibir esses dados adequadamente, adicionando o seguinte ao arquivo styles/global.css.

@tailwind base;
@tailwind components;
@tailwind utilities;

html {
   font-family: "DM Sans", sans-serif;
   background: white;
}

.nft-grid {
 display: grid;
 --template-column-gutters: 8px;
 --template-columns: 1;
 --template-column-compact-multiplier: 1;
 --template-reduced-columns: 0;
 --template-reduced-columns-multiplier: 1;
 gap: var(--template-column-gutters);
 grid-auto-rows: minmax(0px, 1fr);
 grid-template-columns: repeat( calc(var(--template-columns) - (var(--
template-reduced-columns) * var(--template-reduced-columns-multiplier))), minmax(0, 1fr) );
}

@media (min-width: 20rem) {
 .nft-grid {
  --template-columns: 2;
 }
}

@media (min-width: 30rem) {
 .nft-grid {
  --template-columns: 3;
 }
}

@media (min-width: 40rem) {
 .nft-grid {
  --template-columns: 4;
 }
}

@media (min-width: 768px) {
 .nft-grid {
  --template-column-gutters: 16px;
 }
}
Enter fullscreen mode Exit fullscreen mode

Agora substitua o conteúdo de index.tsxpelo código abaixo:

import type { NextPage } from "next";
import { useActiveListings, useContract } from "@thirdweb-dev/react";
import NFTCard from "../components/NFTCard";

const Home: NextPage = () => {
 const { contract } = useContract(
  "SEU CONTRATO DE MERCADO",
  "mercado"
 );

 const { data: nfts, isLoading } = useActiveListings(contract);

 if (isLoading)
  return (
   <div className={"mb-3 flex w-screen justify-center"}>Loading ...</div>
  );

return (
 <div className={"space-y-4 p-2"}>
  <div className={"text-2xl font-semibold"}>Active Listings</div>
  <div className={`nft-grid`}>
   {nfts &&
    nfts.map((nft) => {
     return (
      <a>
       <NFTCard
        nft={{
         name: nft.asset.name as string,
         tokenUri: nft.asset.image as string,
         price: nft.buyoutCurrencyValuePerToken?.displayValue,
        }}
       />
      </a>
     );
    })}
   </div>
  </div>
 );
};

export default Home;
Enter fullscreen mode Exit fullscreen mode

Agora sua página inicial deve se parecer com a imagem abaixo!

alt_text

Agora que temos a página inicial, vamos exibir as listagens individuais de NFT.

Para isso, vamos criar um arquivo chamado: pages/assets/[listingid].tsx.

import { useRouter } from "next/router";
import { useContract, useListing } from "@thirdweb-dev/react";
import Image from "next/image";
import { AiOutlineClockCircle } from "react-icons/ai";
import { BigNumber } from "ethers";

export default function NFT() {
 const router = useRouter();
 const { listingId } = router.query;
 const { contract } = useContract(
  "SEU CONTRATO DE MERCADO",
  "mercado"
 );

 const { data: nft, isLoading } = useListing(contract, listingId as string);

 const buyoutListing = async () => {
  try {
   await contract?.buyoutListing(BigNumber.from(listingId), 1);
  } catch (e) {
   alert(e);
 }
};

if (isLoading || !nft)
 return (
  <div className={"flex h-screen items-center justify-center"}>
   Loading ...
  </div>
 );

return (
 <div className="flex justify-center">
  <div className="flex max-w-[500px] flex-col justify-center gap-y-4 p-2">
   <div className={"text-2xl font-semibold"}>{nft?.asset?.name}</div>
   <div className={"flex flex-col rounded-lg border border-[#e8ebe5]"}>
    <div className={`flex items-center justify-start p-3`}>
     <Image src={`/matic-logo.png`} height={20} width={20} />
    </div>
    <Image
     className={"rounded-2xl"}
     src={nft?.asset.image as string}
     width={500}
     height={500}
     objectFit={"cover"}
    />
   </div>

   <div className={"flex space-x-1 text-sm"}>
    <div className={"text-gray-500"}>Owned by</div>
    <div className="cursor-pointer text-blue-500">
     {nft?.sellerAddress}
    </div>
   </div>

   {/*Bottom Section*/}
   <div className={"flex flex-col rounded-lg border border-[#e8ebe5]"}>
    <div className={"border-b border-[#e8ebe5] p-3"}>
     <div
      className={
       "flex items-center space-x-2 text-sm text-gray-700 md:text-base"
      }
     >
      <AiOutlineClockCircle size={24} />
      <p>Sale ends November 26, 2022 at 7:39pm GMT+11</p
     </div>
    </div>
    <div className={"flex flex-col gap-y-2 bg-slate-50 p-3"}>
     <div className={"text-sm text-gray-500"}>Current Price</div>
     <div className={`flex items-center space-x-3`}>
      <Image src={`/matic-logo.png`} height={24} width={24} />
      <p className={`text-3xl font-semibold`}>
       {nft?.buyoutCurrencyValuePerToken?.displayValue}
      </p>
     </div>
     <button
      type="button"
      className="rounded-lg bg-blue-700 px-5 py-4 text-base font-bold text-white hover:bg-blue-800 focus:outline-none focus:ring-4 focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
      onClick={buyoutListing}
     >
      Purchase
     </button>
    </div>
   </div>
  </div>
 </div>
);
}
Enter fullscreen mode Exit fullscreen mode

Agora adicione o link para esta página no index.tsx.

const Home: NextPage = () => {
...
 return (
...
      <Link
       href={`/assets/${nft.id}`}
       key={nft.assetContractAddress + nft.id} >
       <a>
        <NFTCard
         nft={{
          name: nft.asset.name as string,
          tokenUri: nft.asset.image as string,
          price: nft.buyoutCurrencyValuePerToken?.displayValue,
         }}
        />
       </a>
      </Link>
...
);
};
Enter fullscreen mode Exit fullscreen mode

Agora esta página deve parecer com a imagem abaixo:

alt_text

Agora, quando você clicar em comprar, deve abrir a carteira Metamask.

alt_text

Bônus) Visualizar todos os NFTs em sua carteira

Agora que temos a página inicial e uma página de NFTs, vamos criar uma página que mostra todos os NFTs existentes em nossa carteira. Para isso, a abordagem mais fácil é usar a API Web3 da Moralis. É grátis! Por favor, cadastre-se em seu site e crie sua chave de API.

alt_text

Assim que você tiver suas chaves de API, crie um arquivo .env na pasta raiz e adicione:

MORALIS_API_KEY=<YOUR API KEY>
Enter fullscreen mode Exit fullscreen mode

Vamos utilizar a funcionalidade de backend do Next.js e criar a página pages/api/wallet/[walletAddress]/nfts.ts com o seguinte código:

import { NextApiRequest, NextApiResponse } from "next";
import axios from "axios";

export default async function handle(
 req: NextApiRequest,
 res: NextApiResponse
) {
 const { walletAddress } = req.query;

 const options = {
  method: "GET",
  url: `https://deep-index.moralis.io/api/v2/${walletAddress}/nft`,
  params: { chain: "mumbai", format: "decimal" },
  headers: {
   accept: "application/json",
   "X-API-Key": process.env.MORALIS_API_KEY,
  },
 };

 try {
  const { data } = await axios.request(options);

  const results: { tokenUri: any; name: any }[] = data.result.map(
   (nft: any) => ({
    name: JSON.parse(nft.metadata)?.name,
    tokenUri: JSON.parse(nft.metadata)?.image,
  })
 );

  const filteredResults = results.filter(
   (data) => !!data?.tokenUri && !!data?.name
  );

  res.status(200).json(filteredResults);
 } catch (error) {
  console.error(error);
  res.status(500).json({ error: error });
 }
}
Enter fullscreen mode Exit fullscreen mode

Aqui, nós buscamos os NFTs fornecendo um endereço de carteira. Em seguida, reestruturamos/filtramos os dados e os retornamos para o cliente.

Agora que temos nossa API de NFTs, vamos criar a página pages/profile/[walletAddress].tsx no frontend com o seguinte código:

import { useRouter } from "next/router";
import NFTCard from "../../components/NFTCard";
import Image from "next/image";
import { fetcher } from "../../utils/utils";
import useSWR from "swr";

export default function Profile() {
 const router = useRouter();
 const { walletAddress } = router.query;

 const { data } = useSWR(`/api/wallet/${walletAddress}/nfts`, fetcher);

if (!data)
 return (
  <div className={"flex h-screen w-screen items-center justify-center"}>
   Loading ...
  </div>
 );

return (
 <div className={"flex w-screen flex-col justify-center gap-y-2 p-4"}>
  <div className={"space-y-2"}>
   <div className={"text-3xl font-semibold"}>Sua coleção</div>

   <div>
    <div className={"flex items-center space-x-1"}>
     <Image src={`/matic-logo.png`} height={16} width={16} />

     <p className={"w-1/4 truncate text-slate-400"}>{walletAddress}</p>
   </div>
  </div>
 </div>

 {data?.length === 0 ? (
  <div>Nenhum NFT foi encontrado...</div>
 ) : (
  <div className={`nft-grid`}>
   {data?.map((nft: any, index: number) => {
    return (
     <NFTCard
      key={index}
      nft={{
       name: nft.name
       tokenUri: nft.tokenUri,
      }}
     />
    );
   })}
  </div>
 )}
</div>
);
}
Enter fullscreen mode Exit fullscreen mode

Agora vamos adicionar um link para a página profile/[walletAddress] em nosso index.tsx.

import type { NextPage } from "next";
import {
 useActiveListings,
 useAddress,
 useContract,
} from "@thirdweb-dev/react";
import Link from "next/link";
import NFTCard from "../components/NFTCard";

const Home: NextPage = () => {
 const address = useAddress();

...

 return (
  <div className={"space-y-4 p-2"}>
   <div className={"flex space-x-4"}>
    <div className={"text-2xl font-semibold"}>Listagens ativas</div>
    <Link href={`profile/${address}`}>
     <div className={"cursor-pointer text-2xl font-semibold"}>
     Minhas coleções
     </div>
    </Link>
   </div>

…
Enter fullscreen mode Exit fullscreen mode

E isso é tudo! Você tem seu mercado de NFTs, construído com Next.js e Thirdweb na rede Polygon.

Este guia foi escrito por Zain Manji e Nam Dao, e foi publicado pela primeira vez no blog da Lazer. A Lazer é um estúdio de produtos digitais que projeta, constrói e envia produtos incríveis para algumas das melhores marcas do mundo.

Visite o site da Lazer aqui e entre em contato com eles no Twitter ou envie um e-mail para [email protected].

Esse artigo foi escrito por Zain Manji e Nam Dao e traduzido por Arnaldo Campos. Seu original pode ser lido aqui.

Top comments (0)