In this guide, we will create an app that takes a coupon code to mint an NFT. Only users that have a coupon code can mint!
Intro
Let's say you're launching an NFT collection or you want to reward some of your customers or loyal followers and you want to make sure only they can mint the NFTs.
There are a bunch of ways you can tackle this, but thirdweb has developed the perfect solution: On demand minting
.
You can create your own unique codes and distribute them to whomever you want. With our on demand minting
feature, you will allow only users holding that unique code to mint an NFT.
Check out our example repository with the project built out on Github.
Flow
The user goes to the app and picks an image to mint an NFT or you can pre-set an image.
In this guide, we'll let the user pick their own image.
The user then needs to enter a valid coupon code. Once the code is entered, an internal API call will be made to our backend, where the code will be validated.
If the code is valid, a signed payload is sent to the front-end, allowing the user to mint an NFT.
Setup
Make sure you have an NFT Collection
contract, before you start building this app.
If you don't have one, go ahead to the dashboard and deploy one.
To build this app, we need to walk through the following steps:
- Spin up a starter template from the thirdweb example repo.
- Build our UI
- Build an internal API request
- Build a back-end to validate the code and create
- Build our minting function
Never use your Private Keys
on the front end and never expose your Private Keys
to anyone.
Make sure you are familiar with using Environment Variables
and not committing sensitive information to GitHub (i.e., be familiar with .gitignore
).
Let's go!
Building the App
Please note we won't cover every line of code. We'll take you through the logic and important parts. If you want to recreate the style of this app or certain details, please check out the repo!
Building the UI
In this guide, we're using this starter template. What we want to create is:
npx thirdweb create --next --ts
This template comes with thirdweb and wallet connection pre-configured!
To simplify things, we'll use the unique code as the name of our NFT.
So the input field will be used to both validate the unique code and name the NFT.
const [nftName, setNftName] = useState<string>("");
<input
type="text"
placeholder="Name of your NFT"
className={styles.textInput}
maxLength={26}
onChange={(e) => setNftName(e.target.value)}
/>
To upload the image we want a couple of things.
This app allows the user to drag and drop an image and upload one by clicking in the area.
Next to that, we want to show the image, just to make sure the user has the right image.
const [file, setFile] = useState();
// We need the useRef to create the drag and drop feature
const fileInputRef = useRef(null);
// Function to store file in state when user uploads it
const uploadFile = () => {
if (fileInputRef?.current) {
fileInputRef.current.click();
fileInputRef.current.onchange = () => {
if (fileInputRef?.current?.files?.length) {
const file = fileInputRef.current.files[0];
setFile(file);
}
};
}
};
<div className={styles.collectionContainer}>
{file ? (
<img
src={URL.createObjectURL(file)}
style={{ cursor: "pointer", maxHeight: 250, borderRadius: 8 }}
onClick={() => setFile(undefined)}
/>
) : (
<div
className={styles.imageInput}
onClick={uploadFile}
onDragOver={(e) => e.preventDefault()}
onDrop={(e) => {
e.preventDefault();
setFile(e.dataTransfer.files[0]);
}}
>
Drag and drop an image here to upload it!
</div>
)}
</div>
<input
type="file"
accept="image/png, image/gif, image/jpeg"
id="profile-picture-input"
ref={fileInputRef}
style={{ display: "none" }}
/>
Finall, we'll create a button to mint an NFT. We'll write the mintWithSignature
function below!
<a className="{styles.mainButton}" onClick={mintWithSignature}>
Mint NFT
</a>
Build an internal API request
Now we will take the info from our input fields and pass them to our backend, so our backend can do its magic.
To do that, we'll create an internal API call to our backend.
The address is grabbed from the connected wallet using the useAddress
hook.
Make a request to the API route:
const signedPayloadReq = await fetch(`/api/server`, {
method: "POST",
body: JSON.stringify({
authorAddress: address, // Address of the current user
nftName: nftName,
imagePath: url,
}),
});
The backend
The backend will handle the internal API Request and send back a signed payload if the unique code is valid.
Instantiate the SDK, connect your smart contract and then build the following function.
If you don't know how to instantiate our SDK, check this guide.
If you want to see the full backend code, check out the server.ts
file in the example repo here.
The backend validates the code, by checking if a pre-compiled list contains the code the user has entered.
An example of this list can be found in the repo here. In this case the unique codes are animal names.
if (!animalNames.includes(nftName?.toLowerCase())) {
res.status(400).json({ error: "That's not one of the animals we know!" });
return;
}
Once the unique code is validated, we take the info passed from the front end and generate a unique signature.
Then send that signature to the front-end, which will be used to mint the NFT. Without this unique signature, the minting functionality does not work!
// Generate the signature for the page NFT
const signedPayload = await nftCollection.signature.generate({
to: authorAddress,
metadata: {
name: nftName,
image: imagePath,
description: "An awesome animal NFT",
properties: {
// Add any properties you want to store on the NFT
},
},
});
// Return back the signedPayload to the client.
res.status(200).json({
signedPayload: JSON.parse(JSON.stringify(signedPayload)),
});
Build the minting function
Now we will build the minting function and bind it to our button.
const signedPayload = json.signedPayload;
const nft = await nftCollection.signature.mint(signedPayload);
That's it!
That's how you use unique codes to mint NFTs.
This guide is just an example of how to do it. You can decide what settings you want to apply in your application.
Bear in mind how you deal with your logic and Private Keys. Using logic and Private Keys at the front end exposes you to risk. Never share your Private Keys, and be aware of how you code your logic.