WEB3DEV

Cover image for Crie Um Programa de Fidelidade Usando o Contrato de Cartão Fidelidade
Isabela Curado Nehme
Isabela Curado Nehme

Posted on

Crie Um Programa de Fidelidade Usando o Contrato de Cartão Fidelidade

https://blog.thirdweb.com/content/images/size/w2000/2023/09/Build-a-Loyalty-Program-using-the-Loyalty-Card-Contract--1-.png

Este guia irá lhe dizer tudo o que você precisa saber sobre o novo contrato do Cartão Fidelidade. Posteriormente neste guia, também veremos isso em ação em um aplicativo Next.js, onde os usuários podem gerar e cancelar seus cartões fidelidade, e também construiremos um painel de administração para os administradores atualizarem e revogarem os cartões fidelidade!

Vamos começar!

Veja o código-fonte deste guia aqui:

https://github.com/thirdweb-example/loyalty-card/tree/main?ref=blog.thirdweb.com

O Que é o Contrato do Cartão Fidelidade?

O contrato do Cartão Fidelidade destina-se a ser utilizado para o lançamento de programas de fidelidade. Cada NFT representa um cartão fidelidade e os metadados do NFT contêm as informações do cartão fidelidade. É muito semelhante ao nosso contrato ERC721 NFT Collection pré-construído, mas tem algumas funções adicionais para gerenciar melhor os cartões.

Existe uma função de cancelamento para o proprietário do NFT, para que ele possa cancelar seu cartão, o que queima o NFT.

As funções revoke e update permitem que os administradores revoguem o cartão de um usuário e atualizem os metadados NFT para torná-lo mais/menos valioso com base no status de fidelidade dos usuários. Os cartões são emitidos aos usuários por meio de cunhagem de assinaturas.

Como o administrador tem algum controle extra sobre os NFTs dos usuários, este contrato não é ideal para a maioria das coleções, mas sim para coleções que representam um programa de fidelidade, como associação ou assinatura.

Implante o Contrato do Cartão Fidelidade

Para começar, vá para a página Contracts (Contratos) em seu painel da Thirdweb e clique em Deploy Contract (Implantar Contrato):

https://blog.thirdweb.com/content/images/size/w1000/2023/03/SCR-20230304-uu3-1-.png

Você será direcionado para a página Explore (Explorar) da Thirdweb - onde poderá navegar pelos contratos inteligentes criados pelos principais protocolos da Web3 e implantá-los com apenas alguns cliques!

Nota: você também pode usar a CLI daThirdweb para configurar um projeto de contrato inteligente para criar seus próprios contratos, executando o comando abaixo em seu terminal:


npx thirdweb create contract

Enter fullscreen mode Exit fullscreen mode

Isso o guiará por um fluxo de etapas fáceis de seguir para criar seu contrato. Saiba mais sobre isso em nosso guia CLI.

Caso contrário, vamos voltar ao Explore:

https://blog.thirdweb.com/content/images/size/w1000/2023/09/thirdweb-explore.png

Aqui, selecione o Loyalty Card contract (contrato do Cartão Fidelidade). Isso o levará para a seguinte página:

https://blog.thirdweb.com/content/images/size/w1000/2023/09/loyalty-card-contract-preview.png

Quando estiver nessa página, clique em Deploy Now (Implantar agora) e você verá uma gaveta deslizar à direita. Preencha os parâmetros do contrato aqui:

https://blog.thirdweb.com/content/images/size/w1000/2023/09/fill-in-contract-parameters-of-the-loyalty-contract.png

Por fim, selecione a rede/cadeia para a qual deseja implantar e clique em "Deploy Now". Isso desencadeará duas transações em sua carteira que você precisará confirmar:

https://blog.thirdweb.com/content/images/size/w1000/2023/09/click-confirm-to-deploy-your-loyalty-card-program.png

Assim que as transações forem concluídas, você poderá ver o painel do seu contrato:

https://blog.thirdweb.com/content/images/size/w1000/2023/09/image.png

Não precisamos fazer mais nada com o contrato por enquanto. Vamos usar o contrato em um aplicativo Next.js!

Criando o Aplicativo Next.js

Agora, vamos criar um aplicativo Web onde os usuários possam:

  • Gerar novos cartões fidelidade.
  • Ver seus cartões fidelidade/cancelá-los.

E os administradores podem:

  • Ver todos os cartões fidelidade presentes.
  • Revogar cartões de usuários.
  • Atualizar os metadados dos cartões fidelidade.

Usando a CLI da Thirdweb, crie um novo projeto Next.js e TypeScript com o React SDK, pré-configurado para você, usando o seguinte comando:


npx thirdweb create app --next --ts

Enter fullscreen mode Exit fullscreen mode

💡 É necessária uma chave de API para usar os serviços de infraestrutura da Thirdweb, como armazenamento, RPCs e infraestrutura de carteira inteligente de dentro do SDK. Se você ainda não criou uma chave, poderá fazê-lo gratuitamente no painel da ThirdWeb.

Para usar uma chave de API com o React SDK, passe o clientId para o arquivo ThirdwebProvider. O modelo já vem com o ID do cliente, então você pode criar um novo arquivo .env.local e adicionar o clientId e o secretKey (que usaremos em breve) com os seguintes nomes:


NEXT_PUBLIC_TEMPLATE_CLIENT_ID=

TW_SECRET_KEY=

Enter fullscreen mode Exit fullscreen mode

Agora, adicione também uma chave privada da carteira:


WALLET_PRIVATE_KEY=<your-private-key>

Enter fullscreen mode Exit fullscreen mode

IMPORTANTE: Chaves Privadas.

Usar chaves privadas como variável env não é a melhor prática e é vulnerável a ataques. Estamos usando esse método neste guia por uma questão de brevidade, mas recomendamos fortemente o uso de um gerenciador de segredos para armazenar sua chave privada.

Certifique-se de armazenar e acessar sua chave privada com segurança.

  • Verifique se você precisa usar uma chave privada para sua aplicação.

  • Nunca exponha diretamente sua chave privada em seu código-fonte.

  • Nunca envie nenhum arquivo que possa conter sua chave privada para seu controle de origem.

  • Nunca use uma chave privada para uma aplicação de front-end (site/dApp).

Se você não tiver certeza de como armazenar e acessar sua chave privada com segurança, não prossiga.

Em seguida, precisamos atualizar a cadeia na qual nosso aplicativo funciona. Entre em _app.tsx e altere a variável activeChain para a cadeia na qual você implantou seu contrato. No meu caso, é Mumbai:


// Esta é a chainId em que seu dApp trabalhará.

const activeChain = "mumbai";

Enter fullscreen mode Exit fullscreen mode

Como precisaremos do endereço do contrato em vários locais, crie um novo arquivo consts.ts e adicione o endereço do contrato:


export const CONTRACT_ADDRESS = "0x19B8e6c26C5d278905449bF447f98d133392bB3B";

Enter fullscreen mode Exit fullscreen mode

Criando o Back-end

O back-end do nosso aplicativo irá gerar assinaturas, usando cunhagem de assinaturas, com alguns metadados que a carteira do usuário pode usar para cunhar um cartão. Portanto, crie um novo arquivo nomeado generate-sig.ts na pasta pages/api e na estrutura básica da API. Permitiremos apenas solicitações de postagem para esta API, por isso, se não for uma solicitação de postagem, enviaremos um erro:


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

const handler = async (req: NextApiRequest, res: NextApiResponse) => {

  if (req.method !== "POST") {

    return res.status(405).json({ error: "Método não permitido (Method not allowed)" });

  }

};

export default handler;

Enter fullscreen mode Exit fullscreen mode

Abaixo disso, adicione um bloco try-catch onde inicializamos o SDK Thirdweb usando a chave secreta e a chave privada da carteira:


 try {

    const sdk = ThirdwebSDK.fromPrivateKey(

      process.env.WALLET_PRIVATE_KEY!,

      "mumbai",

      {

        secretKey: process.env.TW_SECRET_KEY!,

      }

    );

  } catch (error) {

    console.error(error);

    return res.status(500).json({ error: "Erro interno de servidor (Internal server error)" });

 }

Enter fullscreen mode Exit fullscreen mode

Em seguida, precisamos acessar o contrato:


const contract = await sdk.getContract(CONTRACT_ADDRESS);

Enter fullscreen mode Exit fullscreen mode

Estou usando a variável do arquivo consts.ts que criamos anteriormente neste guia. Você pode importá-la assim:


import { CONTRACT_ADDRESS } from "../../consts";

Enter fullscreen mode Exit fullscreen mode

Agora, obtemos o endereço do corpo da solicitação e criamos uma carga útil de assinatura usando-o:


const address = req.body.address;

const payload: PayloadToSign721withQuantity = {

  to: address,

  metadata: {

    name: "Meu cartão fidelidade (My loyalty card)",

    description: "Alguma descrição de cartão fidelidade. Estou com muita preguiça para escrever uma (Some loyalty card description. Too lazy to write one).",

    image:    "https://15065ae3c21e0bff07eaf80b713a6ef0.ipfscdn.io/ipfs/bafybeie2mhmbriq4ndtl3i7enkovlm6njycdutobw4jczixdbfoensranm/blue_square.png",

    attributes: [

      {

        trait_type: "color",

        value: "blue",

      },

      {

        trait_type: "points",

        value: 100,

      },

    ],

  },

};
Enter fullscreen mode Exit fullscreen mode

O PayloadToSign721withQuantity é o tipo de carga útil que precisamos passar, iremos importá-la do TypeScript SDK. Fique à vontade para verificar as outras propriedades que você pode passar nos metadados e alterar os valores das que estão aqui.

Por fim, usamos a função contract.erc721.signature.generate para gerar a assinatura e retorná-la:


const signedPayload = await contract.erc721.signature.generate(payload);

return res.status(200).json({ signedPayload });

Enter fullscreen mode Exit fullscreen mode

Sua API final deve ser semelhante a isto:


import { PayloadToSign721withQuantity, ThirdwebSDK } from "@thirdweb-dev/sdk";

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

import { CONTRACT_ADDRESS } from "../../consts";

const handler = async (req: NextApiRequest, res: NextApiResponse) => {

  if (req.method !== "POST") {

    return res.status(405).json({ error: "Método não permitido (Method not allowed)" });

  }

  try {

    const sdk = ThirdwebSDK.fromPrivateKey(

      process.env.WALLET_PRIVATE_KEY!,

      "mumbai",

      {

        secretKey: process.env.TW_SECRET_KEY!,

      }

    );

    const contract = await sdk.getContract(CONTRACT_ADDRESS);

    const address = req.body.address;

    const payload: PayloadToSign721withQuantity = {

      to: address,

      metadata: {

        name: "Meu cartão fidelidade (My loyalty card)",

        description: "Alguma descrição de cartão fidelidade. Estou com muita preguiça para escrever uma (Some loyalty card description. Too lazy to write one).",

        image:

          "https://15065ae3c21e0bff07eaf80b713a6ef0.ipfscdn.io/ipfs/bafybeie2mhmbriq4ndtl3i7enkovlm6njycdutobw4jczixdbfoensranm/blue_square.png",

        attributes: [

          {

            trait_type: "cor (color)",

            value: "azul (blue)",

          },

          {

            trait_type: " pontos (points)",

            value: 100,

          },

        ],

      },

    };

    const signedPayload = await contract.erc721.signature.generate(payload);

    return res.status(200).json({ signedPayload });

  } catch (error) {

    console.error(error);

    return res.status(500).json({ error: "Erro interno de servidor (Internal server error)" });

  }

};

export default handler;
Enter fullscreen mode Exit fullscreen mode

Criando o Front-end do Usuário

Vamos agora construir o front-end para um usuário comum! Para isso, vá para pages/index.tsx. Primeiramente, exclua tudo dentro do main, pois não vamos precisar dele:


import styles from "../styles/Home.module.css";

import { NextPage } from "next";

const Home: NextPage = () => {

  return <main className={styles.main}></main>;

};

export default Home;

Enter fullscreen mode Exit fullscreen mode

Agora, adicionaremos um Web3Button dentro da tag principal, desta forma:


<Web3Button action={() => generate()} contractAddress={CONTRACT_ADDRESS}>

  Generate NFT

</Web3Button>

Enter fullscreen mode Exit fullscreen mode

Como você pode ver, estamos usando uma função de geração externa que ainda não foi criada. Vamos criar isso agora!


 const generate = async () => {

    try {

      const res = await fetch("/api/generate-sig", {

        method: "POST",

        headers: {

          "Content-Type": "application/json",

        },

        body: JSON.stringify({

          address,

        }),

      });

      const data = await res.json();

      await contract?.erc721.signature.mint(data.signedPayload);

      alert("NFT cunhado! (NFT minted!)");

    } catch (err) {

      console.error(err);

    }

  };

Enter fullscreen mode Exit fullscreen mode

A função generate faz uma pós-solicitação à API que criamos anteriormente e, em seguida, usa a assinatura gerada para cunhar o NFT, solicitando ao usuário que confirme uma transação!

Também precisamos adicionar o gancho useContract para acessar nosso contrato:


const { contract } = useContract(CONTRACT_ADDRESS);

Enter fullscreen mode Exit fullscreen mode

Se você tentar cunhar um cartão fidelidade, deve funcionar!

Agora, vamos exibir os cartões do usuário. Primeiramente, adicionaremos os ganchos necessários para obter o endereço do usuário conectado e seus NFTs:


const address = useAddress();

const { data: nfts, isLoading, isError } = useOwnedNFTs(contract, address);

Enter fullscreen mode Exit fullscreen mode

Agora, vamos mapear os NFTs e renderizá-los. Como precisaremos renderizar os NFTs em vários locais, vou criar um componente NFTCard:


{nfts && (

    <div className={styles.nfts}>

      {nfts.map((nft) => (

        <NFTCard nft={nft} key={nft.metadata.id} />

      ))}

    </div>

)}

Enter fullscreen mode Exit fullscreen mode

Vamos então criar um novo arquivo chamado NFTCard.tsx~ na pasta components e adicionar o seguinte:


import { NFT, ThirdwebNftMedia, Web3Button } from "@thirdweb-dev/react";

import Image from "next/image";

import { type FC } from "react";

import { CONTRACT_ADDRESS } from "../consts";

import styles from "../styles/Home.module.css";

interface NFTProps {

  nft: NFT;

}

export const NFTCard: FC<NFTProps> = ({ nft }) => {

  const id = nft.metadata.id;

  return (

    <div key={nft.metadata.id} className={styles.nft}>

      {nft.metadata.image ? (

        <ThirdwebNftMedia metadata={nft.metadata} />

      ) : (

        <Image

          src="https://t3.ftcdn.net/jpg/02/48/42/64/360_F_248426448_NVKLywWqArG2ADUxDq6QprtIzsF82dMF.jpg"

          alt=""

          width="360"

          height="200"

          style={{

            objectFit: "contain",

          }}

        />

      )}

      <h1>{nft.metadata.name}</h1>

      <p>{nft.metadata.description}</p>

      {nft.metadata.attributes && (

        <ul>

          {/* @ts-ignore */}

          {nft.metadata.attributes.map((attribute) => (

            <li key={attribute.trait_type}>

              <strong>{attribute.trait_type}:</strong> {attribute.value}

            </li>

          ))}

        </ul>

      )}

      <Web3Button

        action={(contract) => contract.erc721.cancel(id)}

        contractAddress={CONTRACT_ADDRESS}

      >

        Cancel

      </Web3Button>

    </div>

  );

};

Enter fullscreen mode Exit fullscreen mode

Isso criará um cartão NFT simples que renderiza os metadados do NFT, usando o renderizador ThirdwebNFTMedia da Thirdweb e possui um botão cancelar que chama a função contract.erc721.cancel, que cancela/queima o NFT.

Também adicionei alguns estilos básicos para os NFTs. Você pode copiá-los/alterá-los como quiser:


.nfts {

  display: grid;

  grid-template-columns: repeat(3, 1fr);

  grid-gap: 24px;

  width: 100%;

  margin-top: 3rem;

}

.nft {

  display: flex;

  flex-direction: column;

  align-items: center;

  justify-content: center;

  margin: 0 auto;

  padding: 1rem;

  border: 1px solid #eaeaea;

  border-radius: 10px;

  width: fit-content;

}

Enter fullscreen mode Exit fullscreen mode

Ele renderizará um NFT assim:

https://blog.thirdweb.com/content/images/2023/09/image-1.png

É isso para o lado do usuário. Sinta-se à vontade para ir além no caso de uso, mas, para simplificar o guia, nós o mantivemos simples!

Construindo o Painel de Administração

Vamos em frente e construir o painel de administração! Para isso, crie um novo arquivo admin.tsx na pasta pages. Adicionaremos a estrutura básica aqui também:


import styles from "../styles/Home.module.css";

import { NextPage } from "next";

const Admin: NextPage = () => {

  return <main className={styles.main}></main>;

};

export default Admin;

Enter fullscreen mode Exit fullscreen mode

Então, ao invés do useOwnedNFTs, que usamos na página inicial, usaremos useNFTs, que obtém todos os NFTs do contrato:


const { contract } = useContract(CONTRACT_ADDRESS);

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

Enter fullscreen mode Exit fullscreen mode

Podemos então reutilizar o NFTCard novamente para renderizar os cartões no contrato:


 {nfts && (

        <div className={styles.nfts}>

          {nfts.map((nft) => (

            <NFTCard nft={nft} key={nft.metadata.id} adminView={true} />

          ))}

        </div>

      )}

Enter fullscreen mode Exit fullscreen mode

Se você notar, adicionamos outra propriedade chamada adminView, que também precisa ser adicionada no componente NFTCard, por isso, vamos adicionar essa lógica! Primeiramente, iremos adicioná-la ao tipo e aceitá-la como uma prop:


interface NFTProps {

  nft: NFT;

  adminView?: boolean;

}

export const NFTCard: FC<NFTProps> = ({ nft, adminView }) => {

Enter fullscreen mode Exit fullscreen mode

Em seguida, adicionaremos uma condição para verificar se adminView está habilitado. Se estiver, precisamos renderizar algumas informações extras e um botão de revogação; caso contrário, um simples botão de cancelamento:


    {adminView ? (

        <>

          <p>

            <strong>Token ID:</strong> {nft.metadata.id}

          </p>

          <p>Owner: {nft.owner}</p>

          {nft.owner !== "0x0000000000000000000000000000000000000000" && (

            <Web3Button

              action={(contract) => contract.erc721.revoke(id)}

              contractAddress={CONTRACT_ADDRESS}

            >

              Revoke

            </Web3Button>

          )}

        </>

      ) : (

        <Web3Button

          action={(contract) => contract.erc721.cancel(id)}

          contractAddress={CONTRACT_ADDRESS}

        >

          Cancel

        </Web3Button>

      )}

Enter fullscreen mode Exit fullscreen mode

A seguir, vamos adicionar a capacidade de editar a propriedade de pontos de um cartão fidelidade.

Para isso, primeiro, adicione dois ganchos useState para armazenar o novo valor dos pontos e o estado de edição:


const [points, setPoints] = useState(0);

const [isEditing, setIsEditing] = useState(false);

Enter fullscreen mode Exit fullscreen mode

Em seguida, adicione a funcionalidade de edição abaixo do botão revoke (revogar):


 {isEditing ? (

            <>

              <input

                type="number"

                value={points}

                onChange={(e) => setPoints(Number(e.target.value))}

              />

              <Web3Button

                action={() => update()}

                contractAddress={CONTRACT_ADDRESS}

              >

                Update

              </Web3Button>

            </>

          ) : (

            <button

              onClick={() => setIsEditing(true)}

              className={styles.button}

            >

              Edit

            </button>

          )}

Enter fullscreen mode Exit fullscreen mode

Aqui, estamos verificando se isEditing é verdade. Se for, então mostramos uma entrada e um Web3Button de atualização, que chama uma função update (escreveremos isso a seguir). Caso contrário, um simples botão para alterar o estado de edição.

Depois, vamos adicionar a função de atualização para que o administrador possa atualizar os pontos quando estiver no modo de edição:


 const { contract } = useContract(CONTRACT_ADDRESS);

  const update = async () => {

    try {

      const metadata = {

        ...nft.metadata,

        attributes: [

          // @ts-ignore

          ...nft.metadata.attributes.filter(

            // @ts-ignore

            (attribute) => attribute.trait_type !== "points"

          ),

          {

            trait_type: "points",

            value: points,

          },

        ],

      };

      await contract?.erc721.update(nft.metadata.id, metadata);

    } catch (err) {

      console.error(err);

    } finally {

      setIsEditing(false);

    }

  };

Enter fullscreen mode Exit fullscreen mode

Aqui, estou apenas alterando os pontos do cartão e mantendo os outros metadados iguais ao que eram anteriormente, mas você pode adicionar mais entradas da mesma maneira para atualizar os outros metadados também.

Se você verificar a página de administração, poderá ver um cartão NFT semelhante a este:

https://blog.thirdweb.com/content/images/size/w1000/2023/09/image-2.png

Conclusão

Este guia nos ensinou como implantar um contrato de cartão fidelidade, construir um front-end de usuário para gerar e cancelar o cartão e um painel de administração para revogar e editar os cartões dos usuários.

Você aprendeu muito. Agora, celebre e compartilhe seus aplicativos incríveis conosco no Discord da Thirdweb!

Precisa de Ajuda?

Para obter apoio, junte-se ao servidor oficial da Thirdweb no Discord ou compartilhe suas idéias em nosso quadro de comentários.

Este artigo foi escrito por Avneesh Agarwal e traduzido por Isabela Curado Nehme. Seu original pode ser lido aqui.

Top comments (0)