In this guide, we ask users to answer some questions about thirdweb before they can mint an NFT using signature-based minting!
-
Edition: to create an ERC721 NFT Collection that our community can mint NFTs into.
-
React SDK: to enable users to connect and disconnect their wallets to our website, using useMetamask & useDisconnect, and prompt them to approve transactions with MetaMask.
-
TypeScript SDK: 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 admin wallet, using our wallet's private key.
- Create a project using this example by running:
npx thirdweb create --template quiz
- Create your Edition using the thirdweb dashboard
- Create an environment variable in a
.env.local
file with your private key, in the formPRIVATE_KEY=xxx
, similarly to the.env.example
file provided. - Ensure to change the contract addresses to your own!
Signature-based minting allows you to grant mint signatures from an admin wallet (that has the minter
role) in the contract, and have another wallet mint an NFT that you specify in your contract.
import { ChainId, ThirdwebProvider } from "@thirdweb-dev/react";
// This is the chainId your dApp will work on.
const activeChainId = ChainId.Goerli;
function MyApp({ Component, pageProps }: AppProps) {
return (
<ThirdwebProvider desiredChainId={activeChainId}>
<Component {...pageProps} />
</ThirdwebProvider>
);
}
Over at the home page at index.tsx
, we're using the thirdweb React SDK MetaMask Connector so our user can connect their wallet to our website.
import {
useAddress,
useDisconnect,
useMetamask,
useNFTCollection,
} from "@thirdweb-dev/react";
// Helpful thirdweb hooks to connect and manage the wallet from metamask.
const address = useAddress();
const connectWithMetamask = useMetamask();
const isOnWrongNetwork = useNetworkMismatch();
const [, switchNetwork] = useNetwork();
};
We have two stateful variables to store which questions the user is on and if they have failed the quiz:
const [questionNumber, setQuestionNumber] = useState(0);
const [failed, setFailed] = useState(false);
The QuizContainer component stores an array of questions, and their possible answers.
The current question (questionNumber
) the user is on is shown in the QuizQuestion component.
The user can click one of the answers to select it.
- If they get it correct, they see the next question
- If they get it wrong, they fail the quiz!
When the user finishes the final question and got all questions correct, they see the Mint
NFT button which triggers the signature-based minting process outlined below.
The way that our signature-based minting process works is in 3 steps:
-
The connected wallet calls a Next JS API Route.
-
The API route generates a signature to mint an NFT based on token ID
0
's metadata in the Edition contract. -
Once the API function is done processing, it sends the client/user a signature. The user can call
mint
with this signature to create an NFT with the conditions provided by our server.
To create an API Route, you'll need to create a file in the /pages/api
directory of your project.
On the server-side API route, we can:
- Run a few checks to see if the requested NFT meets our criteria
- Generate a signature to mint the NFT if it does.
- Send the signature back to the client/user if the NFT is eligible.
Initialize the Thirdweb SDK on the server-side
// Initialize the Thirdweb SDK on the serverside
const sdk = ThirdwebSDK.fromPrivateKey(
// Your wallet private key (read it in from .env.local file)
process.env.PRIVATE_KEY as string,
"goerli"
);
Load the NFT Collection via it's contract address using the SDK
const nftCollection = sdk.getEdition(
// Replace this with your Edition contract address
"0x000000000000000000000000000000000000000"
);
Generate a signature to mint the NFT
// Generate the signature for the page NFT
const signedPayload = await editionContract.signature.generateFromTokenId({
quantity: 1,
tokenId: 0,
to: address,
});
Return the signature to the client
// Return back the signedPayload to the client.
res.status(200).json(signedPayload);
If at any point this process fails or the request is not valid, we send back an error response instead of the generated signature.
res.status(500).json({ error: `Server error ${e}` });
With our API route available, we make fetch
requests to this API, and securely run that code on the server-side.
Call the API route on the client
// Make a request to /api/server
const signedPayloadReq = await fetch(`/api/server`, {
method: "POST",
body: JSON.stringify({
address: address, // Address of the current user
}),
});
Mint the NFT with the signature
// Now we can call signature.mint and pass in the signed payload that we received from the server.
// This means we provided a signature for the user to mint an NFT with.
const nft = await nftCollection?.signature.mint(signedPayload);
For any questions, suggestions, join our discord at https://discord.gg/thirdweb.