Skip to main content

Mint NFTs for VIPs Using unique codes!

· 6 min read
Raza Zaidi

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 send 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:

  1. Spin up a starter template from the thirdweb example repo.
  2. Build our UI
  3. Build an internal API request
  4. Build a back-end to validate the code and create
  5. Build our minting function

IMPORTANT, 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:

  1. A field to input the code
  2. A way to upload an image
  3. A button to mint an NFT

Please note, we obviously want a Connect Wallet button too, but that's already configured for us in the starter template!

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. Feel free to split this. For the input field we'll use a simple useState and some simple HTML tags.

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);
}
};
}
};
{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>
<input
type="file"
accept="image/png, image/gif, image/jpeg"
id="profile-picture-input"
ref={fileInputRef}
style={{ display: "none" }}
/>

Finally we'll create a button to mint an NFT. However we will bind the actual function after coding our backend

<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. Side note, you might think, where are we getting the address from. The address is grabbed from the connected wallet and the starter template is configured with a method to grab the address from the connected wallet!

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 your 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 an 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);

Checks

The code above is shown without the checks build inside the app. Check like to make sure that the API call has been send properly or whether the NFT is minted, etc. If you want to see the checks, check out the repo!

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.