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):
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
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:
Aqui, selecione o Loyalty Card contract (contrato do Cartão Fidelidade). Isso o levará para a seguinte página:
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:
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:
Assim que as transações forem concluídas, você poderá ver o painel do seu contrato:
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
💡 É 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=
Agora, adicione também uma chave privada da carteira:
WALLET_PRIVATE_KEY=<your-private-key>
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";
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";
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;
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)" });
}
Em seguida, precisamos acessar o contrato:
const contract = await sdk.getContract(CONTRACT_ADDRESS);
Estou usando a variável do arquivo consts.ts
que criamos anteriormente neste guia. Você pode importá-la assim:
import { CONTRACT_ADDRESS } from "../../consts";
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,
},
],
},
};
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 });
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;
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;
Agora, adicionaremos um Web3Button
dentro da tag principal, desta forma:
<Web3Button action={() => generate()} contractAddress={CONTRACT_ADDRESS}>
Generate NFT
</Web3Button>
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);
}
};
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);
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);
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>
)}
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>
);
};
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;
}
Ele renderizará um NFT assim:
É 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;
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);
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>
)}
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 }) => {
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>
)}
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);
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>
)}
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);
}
};
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:
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.
Oldest comments (0)