Reward your community using NFTs and thirdweb's signature based minting.

Overview

Community Rewards Example

Introduction

In this guide, we will utilize signature-based minting of NFTs as a mechanism to reward users of a specific community. We connect user's with their Discord account, and generate signatures for an NFT if the user is a member of the Discord server.

Check out the Demo here: https://community-rewards.thirdweb-example.com

If you're interested in reading the basics of signature-based minting, we recommend starting with this example repository: https://github.com/thirdweb-example/signature-based-minting-next-ts

Tools:

  • thirdweb React SDK: To connect to our NFT Collection Smart contract via React hooks such as useNFTCollection, and allow users to sign in with useMetamask.

  • thirdweb NFT Collection: This is the smart contract that our NFTs will be created into.

  • thirdweb TypeScript SDK: To mint new NFTs with signature based minting!

  • Next JS API Routes: For us to securely generate signatures on the server-side, on behalf of our wallet, using our wallet's private key. As well as making server-side queries to the Discord APIs with the user's access token to view which servers they are part of.

  • Next Auth: To authenticate with Discord and access the user's Discord data such as their username, and which servers they are members of.

Using This Repo

  • Create an NFT Collection contract via the thirdweb dashboard on the Polygon Mumbai (MATIC) test network.

  • Create a project using this example by running:

npx create-tw-app --example community-rewards
  • Find and replace our demo NFT Collection address (0xb5201E87b17527722A641Ac64097Ece34B21d10A) in this repository with your NFT Collection contract address from the dashboard.

  • We use the thirdweb discord server ID 834227967404146718. Find and replace instances of this ID with your own Discord server ID. You can learn how to get your Discord server ID from this guide.

npm install
# or
yarn install
  • Run the development server:
npm run start
# or
yarn start

Guide

This project uses signature-based minting to grant mint signatures to wallets who meet a certain set of criteria.

You can see the basic flow of how signature based minting works in this application below:

Signature Based Minting Diagram

In this example, we use signature-based minting to exclusively grant signatures to users who are members of the Discord server with ID 834227967404146718; the thirdweb discord server.

The general flow of the application is this:

  1. User connects their wallet with MetaMask
  2. User authenticates / signs in with Discord
  3. User attempts mint function
  4. Server checks if user is a member of the Discord server
  5. If the user is a member, the server generates a signature for the user's wallet
  6. The server sends the signature to the client
  7. The client uses the signature to mint an NFT into their wallet

In the below sections, we'll outline how each of these steps work and explain the different parts of the application.

Connecting With Metamask

We have a component that handles the sign in logic for both MetaMask and Discord in /components/SignIn.js.

For the MetaMask connection, we are using the useMetamask hook from the thirdweb React SDK.

const connectWithMetamask = useMetamask();

This works because we have the ThirdwebProvider setup in our /pages/app.js file, which allows us to use all of the thirdweb React SDK's helpful hooks.

// This is the chainId your dApp will work on.
const activeChainId = ChainId.Mumbai;

function MyApp({ Component, pageProps }) {
  return (
    <ThirdwebProvider desiredChainId={activeChainId}>
      {/* Next Auth Session Provider */}
      <SessionProvider session={pageProps.session}>
        <Component {...pageProps} />
      </SessionProvider>
    </ThirdwebProvider>
  );
}

Connect with Discord

We are using the Authentication library NextAuth.js to authenticate users with their Discord accounts.

NextAuth uses the pages/api/auth/[...nextauth].js file to handle the authentication logic such as redirects for us.

We setup the Discord Provider and pass in our Discord applications information that we got from the Discord Developer Portal (discussed below).

  providers: [
    DiscordProvider({
      clientId: process.env.CLIENT_ID,
      clientSecret: process.env.CLIENT_SECRET,
      authorization: { params: { scope: "identify guilds" } },
    }),
  ],

As you can see, we are also requesting additional scope on the user's profile called identify guilds.

This is so that we can later make another API request an access which servers the user is a member of.

Setting Up Your Discord Application

Head to the Discord Developer portal and create a new application.

Under the Oauth2 tab, copy your client ID and client secret. We need to store these as environment variables in our project so that we can use them on the API routes in our application.

Create a file at the root of your project called .env.local and add the following lines:

CLIENT_ID=<your-discord-client-id-here>
CLIENT_SECRET=<your-discord-client-secret-here>

Back in the Discord portal, under the Redirects section, you need to add the following value as a redirect URI:

http://localhost:3000/api/auth/callback/discord

When you deploy to production, you will need to do the same again; and replace the http://localhost:3000/ with your domain.

In the SignIn component, we are importing functions from next-auth/react to sign in and out with Discord.

import { useSession, signIn, signOut } from "next-auth/react";

We then user is signed in, we can access their session information using the useSession hook:

const { data: session } = useSession();

One final detail on the Discord connection is that we have some custom logic to append the accessToken to the session, so that we can use this to make further API requests. i.e. we need the user's access token to provide to the Authorization Bearer when we make the API request to see which servers this user is a part of.

// Inside [...nextauth.js]

// When the user signs in, get their token
  callbacks: {
    async jwt({ token, account }) {
      // Persist the OAuth access_token to the token right after signin
      if (account) {
        token.accessToken = account.access_token;
      }
      return token;
    },

    // When we ask for session info, also get the accessToken.
    async session({ session, token, user }) {
      // Send properties to the client, like an access_token from a provider.
      session.accessToken = token.accessToken;
      return session;
    },
  },

Now when we call useSession or getSession, we have access to the accessToken of the user; which allows us to make further requests to the Discord API.

Checking User's Discord Servers

Before the user see's the mint button, we make a check to see if the user is a member of the Discord server, using Next.js API Routes.

This logic is performed on the pages/api/check-is-in-server.js file.

First, we get the user's accessToken from the session.

We use this accessToken to request which servers the user is a member of.

// Get the Next Auth session so we can use the accessToken as part of the discord API request
const session = await getSession({ req });
// Read the access token from the session
const accessToken = session?.accessToken;

// Make a request to the Discord API to get the servers this user is a part of
const response = await fetch(`https://discordapp.com/api/users/@me/guilds`, {
  headers: {
    Authorization: `Bearer ${accessToken}`,
  },
});

// Parse the response as JSON
const data = await response.json();

Now we have all the servers the user is a member of inside the data variable. We can filter the array of servers to find the one we are looking for:

// Put Your Discord Server ID here
const discordServerId = "834227967404146718";

// Filter all the servers to find the one we want
// Returns undefined if the user is not a member of the server
// Returns the server object if the user is a member
const thirdwebDiscordMembership = data?.find(
  (server) => server.id === discordServerId
);

// Return undefined or the server object to the client.
res.status(200).json({ thirdwebMembership: thirdwebDiscordMembership });

We then make a fetch request on the client to this API route on the index.js file:

// This is simply a client-side check to see if the user is a member of the discord in /api/check-is-in-server
// We ALSO check on the server-side before providing the signature to mint the NFT in /api/generate-signature
// This check is to show the user that they are eligible to mint the NFT on the UI.
const [data, setData] = useState(null);
const [isLoading, setLoading] = useState(false);
useEffect(() => {
  if (session) {
    setLoading(true);
    // Load the check to see if the user  and store it in state
    fetch("api/check-is-in-server")
      .then((res) => res.json())
      .then((d) => {
        setData(d);
        setLoading(false);
      });
  }
}, [session]);

We use this information on the client to show either a mint button or a Join Server button to the user:

data ? (
  <div>
    <h3>Hey {session?.user?.name} 👋</h3>
    <h4>Thanks for being a member of the Discord.</h4>
    <p>Here is a reward for you!</p>

    <button onClick={mintNft}>Claim NFT</button>
  </div>
) : (
  <div>
    <p>Looks like you are not a part of the Discord server.</p>
    <a href={`https://discord.com/invite/thirdweb`}>Join Server</a>
  </div>
);

Now the user can either make another request to mint the NFT, or join the Discord server.

Signature Based Minting

On the client-side, when the user clicks the Mint button, we make a request to the generate-signature API route to ask the server to generate a signature for us to use to mint an NFT.

// Make a request to the API route to generate a signature for us to mint the NFT with
const signature = await fetch(`/api/generate-signature`, {
  method: "POST",
  body: JSON.stringify({
    // Pass our wallet address (currently connected wallet) as the parameter
    claimerAddress: address,
  }),
});

The API runs the same check as described above, where we utilize the session's accessToken to ensure the user is a part of the Discord server before generating a signature.

// ... Same Discord API Checks as above.

// Return an error response if the user is not a member of the server
// This prevents the signature from being generated if they are not a member
if (!discordMembership) {
  res.status(403).send("User is not a member of the discord server.");
  return;
}

If the user is a member of the server, we can start the process of generating the signature for the NFT.

Firstly, we initialize the thirdweb SDK using our private key.

// Initialize the Thirdweb SDK on the serverside using the private key on the mumbai network
const sdk = ThirdwebSDK.fromPrivateKey(process.env.PRIVATE_KEY, "mumbai");

You'll need another entry in your .env.local file, containing your private key for this to work.

IMPORTANT: Never use your private key value outside of a secured server-side environment.

PRIVATE_KEY=<your-private-key-here>

Next, we get our NFT collection contract:

// Load the NFT Collection via it's contract address using the SDK
const nftCollection = sdk.getNFTCollection(
  "0xb5201E87b17527722A641Ac64097Ece34B21d10A"
);

And finally generate the signature for the NFT:

We use the information of the user's Discord profile for the metadata of the NFT! How cool is that?

// Generate the signature for the NFT mint transaction
const signedPayload = await nftCollection.signature.generate({
  to: claimerAddress,
  metadata: {
    name: `${session.user.name}'s Thirdweb Discord Member NFT`,
    image: `${session.user.image}`,
    description: `An NFT rewarded to ${session.user.name} for being a part of the thirdweb community!`,
  },
});

And return this signature back to the client:

// Return back the signedPayload (mint signature) to the client.
res.status(200).json({
  signedPayload: JSON.parse(JSON.stringify(signedPayload)),
});

The client uses this signature to mint the NFT that was generated on the server back on index.js:

// If the user meets the criteria to have a signature generated, we can use the signature
// on the client side to mint the NFT from this client's wallet
if (signature.status === 200) {
  const json = await signature.json();
  const signedPayload = json.signedPayload;

  // Use the signature to mint the NFT from this wallet
  const nft = await nftCollectionContract?.signature.mint(signedPayload);
}

Voilà! You have generated a signature for an NFT on the server-side, and used the signature to mint that NFT on the client side! Effectively, restricting access to an exclusive set of users to mint NFTs in your collection.

Going to production

In a production environment, you need to have an environment variable called NEXTAUTH_SECRET for the Discord Oauth to work.

You can learn more about it here: https://next-auth.js.org/configuration/options

You can quickly create a good value on the command line via this openssl command.

openssl rand -base64 32

And add it as an environment variable in your .env.local file:

NEXTAUTH_SECRET=<your-value-here>

Join our Discord!

For any questions, suggestions, join our discord at https://discord.gg/cd thirdweb.

You might also like...

FlipVerse: Blockchain-based eCommerce warranty system using NFTs

FlipVerse:     Blockchain-based eCommerce warranty system using NFTs

FlipVerse 🔗 Blockchain-based eCommerce warranty system using NFTs DEMO LINK About the Site 🚀 Blockchain-based eCommerce warranty system using NFTs T

Dec 22, 2022

Yet another library for generating NFT artwork, uploading NFT assets and metadata to IPFS, deploying NFT smart contracts, and minting NFT collections

eznft Yet another library for generating NFT artwork, uploading NFT assets and metadata to IPFS, deploying NFT smart contracts, and minting NFT collec

Sep 21, 2022

This is NFT minting and marketplace website.

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

Oct 10, 2022

An implementation of ERC1155D, a record setter for minting and transfer gas efficiency.

ERC1155D An implementation of ERC1155D, a record setter for minting and transfer gas efficiency. This contract is in alpha stage and has not been audi

Dec 26, 2022

✨ An IRL tokenization platform to turn your hopes, dreams, and desires into fundable NFTs on the Polygon blockchain using Chainlink, IPFS, Moralis, and NFT.Storage.

✨ An IRL tokenization platform to turn your hopes, dreams, and desires into fundable NFTs on the Polygon blockchain using Chainlink, IPFS, Moralis, and NFT.Storage.

GoFundYourself Getting funding for your passion project, needs or dream doesn't have to be a nightmare! check out our live demo on Netlify Let's Fundi

Dec 6, 2022

Bootstrap an NFT minting site with Merkle tree whitelists.

🖌️ nft-merkle-whitelist-scaffold Bootstrap an NFT minting site with merkle tree whitelists. Go to nft-merkle-whitelist.vercel.app to see the latest d

Dec 24, 2022

Elven Tools Dapp - Elrond blockckchain frontend dapp demo. Primarily for NFT minting, but it can be used for other purposes.

Elven Tools Dapp Docs: elven.tools/docs/landing-page.html Demo: dapp-demo.elven.tools Sneak peek: youtu.be/ATSxD3mD4dc The Dapp is built using Nextjs

Jan 1, 2023

Ethereum NFT minting bot 🍌 🔥

Mineth – Open source Ethereum bot for minting NFTs. This repository provides a code for deploying it on server and calling from your local network. Fo

Aug 6, 2022

CandyPay SDK lets you effortlessly create NFT minting functions for Candy Machine v2 collections.

@candypay/sdk CandyPay SDK lets you effortlessly create NFT minting functions for Candy Machine v2 collections. Simulate minting transactions for mult

Nov 16, 2022
NFTKastle is an NFT marketplace where users can mint their pictures as NFTs, list their NFTs for sale, and buy NFTs from other users.

NFTKastle NFTKastle is an NFT marketplace where users can mint their pictures as NFTs, list their NFTs for sale, and buy NFTs from other users. NFTKas

Paschal 2 Oct 31, 2022
Lazy minting of ERC721 NFTs using EIP712 standard for typed, structured data. ✨

ERC721 - Signature minting Lazy minting of ERC721 NFTs using EIP712 standard for typed, structured data. ✨ How it works Lazy minting or Signature mint

Sunrit Jana 21 Oct 20, 2022
A community website built by the community for the community (Hacktoberfest 2022) :tada:

Hacktoberfest 2022 ?? : Built by the community for the community! This repository is an initiative which aims to help beginners kickstart their open-s

Your First Open Source Project 5 Oct 12, 2022
Daily reward and ROI calculator for Nintia Estate players.

Nintia Estate Calculator The Nintia Estate Calculator is a simple calculator for Nintia Estate players. The calculator allows players to calculate the

Angel Orozco 9 Jun 30, 2022
This Lens Protocol module allows you to create a Transparent Promotion system in which the post creator can add a reward for who (ex: influencers) mirror it.

promote-module (in progress) This Lens Protocol module allows you to create a Transparent Promotion system in which the post creator can add a reward

Alessandro Manfredi 9 Oct 2, 2022
Hasbik is a community based social token and the new paradigm in the crypto space. With the goal to build a community around a crypto token.

Hasbik is a community based social token and the new paradigm in the crypto space. With the goal to build a community around a crypto token.

null 2 Jan 5, 2022
I'm trying to create simple program for adding the digital signature to a pdf file with self-signed certificate. I use node-signpdf and pdf-lib library.

pdf-digital-signature-with-node-signpdf-ejs I'm trying to create simple program for adding the digital signature to a pdf file with self-signed certif

null 5 Dec 25, 2022
Create your own custom NFT minting page using thirdweb's NFT Drop contract

Customizable NFT Drop Minting Page In this example, you can create your own NFT Drop minting page just by customising the template with your branding,

thirdweb examples 59 Dec 24, 2022
Node JS utility to check the signature of Apple P12 Certificates.

CertCheck Node JS utility to check the signature of Apple P12 Certificates. Confirmed to work on macOS and Linux. Windows may need slight changes. Wor

Jailbreaks.app Team 10 Dec 24, 2022