Skip to main content

Sell your NFT in multiple currencies at the same time

· 9 min read
Avneesh Agarwal

Learn how to make an ERC1155 drop where users can pay with multiple currencies using signature minting, Next.js, and thirdweb

Introduction

In this guide, we are going to create an edition collection that will allow users to buy an NFT in multiple currencies. All this is going to happen with Next.js and thirdweb. We are going to use the signature-based minting feature of thirdweb.

Sounds cool, right!? Let's get started.

Setup

I am going to use the Next typescript starter template of thirdweb for this guide.

You can get started with the thirdweb CLI:

npx thirdweb create --next --ts

By default the network in _app.tsx is Mainnet, we need to change it to Mumbai:

_app.tsx
import type { AppProps } from "next/app";
import { ChainId, ThirdwebProvider } from "@thirdweb-dev/react";

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

function MyApp({ Component, pageProps }: AppProps) {
return (
<ThirdwebProvider desiredChainId={activeChainId}>
<Component {...pageProps} />
</ThirdwebProvider>
);
}

export default MyApp;

Creating an edition and NFT

We also need to create an edition and an NFT inside of the edition to be able to mint them to the users.

So, go to the thirdweb dashboard and create an edition contract on the Mumbai (MATIC) network.

Fill out the details and deploy the contract!

Deploy edition contract

Now, let's create and mint a new NFT inside of it. Fill out the details and make sure to set the initial supply to 0.

Mint New NFT

Building the NFT card

I am going to create a simple card to display the NFT, which will have the name, and description of the NFT. A dropdown to be able to change the currency and a button to mint the NFT! At this point, your pages/index.tsx file should look something like this:

import { useAddress, useDisconnect, useMetamask } from "@thirdweb-dev/react";
import type { NextPage } from "next";

const Home: NextPage = () => {
const address = useAddress();
const connectWithMetamask = useMetamask();
const disconnectWallet = useDisconnect();

return (
<div>
{address ? (
<>
<button onClick={disconnectWallet}>Disconnect Wallet</button>
<p>Your address: {address}</p>
</>
) : (
<button onClick={connectWithMetamask}>Connect with Metamask</button>
)}
</div>
);
};

export default Home;

Instead of showing the disconnect Wallet button and wallet address, we will now show the card instead. So, replace everything inside the fragment (<>) with the following:

<div>
<img
src={nft.image.replace("ipfs://", "https://ipfs.thirdweb.com/ipfs/")}
alt={nft.name}
width="200px"
height="100%"
/>
<div>
<p>Name: {nft.name}</p>
<p>Description: {nft.description}</p>
<select>
{nft.tokens.map((token) => (
<option key={token.name} value={token.name}>
{token.name}
</option>
))}
</select>
<button onClick={() => {}}>Mint</button>
</div>
</div>

You might be wondering where are we getting the NFT object, we are going to create a new file and store the NFT object there to keep our code clean.

So, create a new file nft.ts and add this:

import { NATIVE_TOKEN_ADDRESS } from "@thirdweb-dev/sdk";

export const nft = {
name: "My cool NFT",
description: "This is the description for my cool NFT",
image: "ipfs://QmRdoPRtfb6MHLQgsBsC5WVDAf7BZijykYUefEmt6UrTTn/0.png",
id: "0",
tokens: [
{
name: "MATIC",
address: NATIVE_TOKEN_ADDRESS,
price: 5,
},
{
name: "USDC",
address: "0xe11A86849d99F524cAC3E7A0Ec1241828e332C62",
price: 7,
},
],
};

Here, we are creating and exporting a new nft object with some properties like name, description, image, etc.

The tokens array will contain the different currencies using which we can mint the NFTs.

You need to specify a name, address, and price for the token here. We are getting the NATIVE_TOKEN_ADDRESS from the thirdweb package.

Finally, import nft from this file in index.tsx:

import { nft } from "../nft";

If you now go to http://localhost:3000/ you will be able to see a card like this:

NFT Card

I haven't styled this so it doesn't look that beautiful but feel free to customize it for use and make it beautiful ;)!

Storing the currency

We need to store the currency which the user has selected. So, we will create a new state and change it every time the value of the dropdown changes. To do this use the useState hook and pass in the initial value as "MATIC".

const [tokenName, setTokenName] = useState("MATIC");

For updating it inside the onChange event we will use the setTokenName function.

<select onChange={(e) => setTokenName(e.target.value)}>
{nft.tokens.map((token) => (
<option key={token.name} value={token.name}>
{token.name}
</option>
))}
</select>

This will now update the currency so we can use it in the minting process.

Creating an api for generating the signature

Inside the pages folder, create a new folder api and generate-mint-sig.ts inside it. We will create our whole api for generating the mint signature here!

Let's start by building a basic api.

api/generate-mint-sig.ts
import type { NextApiRequest, NextApiResponse } from "next";

const generateMintSignature = async (
req: NextApiRequest,
res: NextApiResponse,
) => {
res.send("Gm wagmi");
};

export default generateMintSignature;

This creates a basic api for us, if you now go to the api/generate-mint-sig endpoint you will get a response of Gm wagmi.

Api response

Let's now initialize the SDK!

const sdk = ThirdwebSDK.fromPrivateKey(
// Learn more about securely accessing your private key: https://portal.thirdweb.com/web3-sdk/set-up-the-sdk/securing-your-private-key
"<your-private-key-here>",
"mumbai",
);
danger

Ensure you store and access your private key securely.

  • Never commit any file that may contain your private key to your source control.

Learn more about securely accessing your private key.

Let's see how to get this private key value.

Getting the wallet private key

In your metamask wallet, click on the three dots, then click account details. You will see an option to export private key there. Export your private key and paste it into the PRIVATE_KEY variable.

Export private key

This will give full access to your wallet to make sure to keep it secret!

We also need to import ThirdwebSDK:

import { ThirdwebSDK } from "@thirdweb-dev/sdk";

Generating the signature for mint

From the frontend we will get the tokenName, tokenId, and the address of the person we are going to mint the NFT to. I am only getting these parameters to make sure that people can't manipulate the data. So, get these parameters from the response body:

const { tokenId, address, tokenName } = JSON.parse(req.body);

We also need access to the edition functions, so get the edition like this:

const edition = sdk.getEdition("YOUR_EDITION_ADDRESS");

We can now use this to generate a mint signature. So, add in this try/catch block:

try {
const signedPayload = await edition.signature.generateFromTokenId({
tokenId: tokenId,
quantity: "1",
metadata: "",
to: address,
currencyAddress: token?.address,
price: token?.price,
});

res.status(200).json({
signedPayload: signedPayload,
});
} catch (err) {
console.log(err);
res.status(500).json({
error: err,
});
}

In this block we are using the edition.signature.generate function provided by thirdweb to create a custom signature for all the user's transactions. We are passing in the tokenId, quantity, metadata, to, currencyAddress, and price of the NFT. If it was successful we will return the signedPayload to the frontend otherwise the error will be returned.

You also need to remove the res.send("Gm wagmi") code block 🥺.

Right now you would be getting an error because we haven't yet added the token so we will create a new variable for getting the token the user is going to use to pay with:

const getToken = (tokenName: string) => {
return nft.tokens.find((t) => t.name === tokenName);
};

const token = getToken(tokenName) ? getToken(tokenName) : getToken("MATIC");

In this variable, we are getting the token from the nft.tokens array. If someone passes a token that is not on the list we will use the MATIC token.

We also need to import nft:

import { nft } from "../../nft";

The final api/generate-mint-sig.ts should look like this:

import { ThirdwebSDK } from "@thirdweb-dev/sdk";
import type { NextApiRequest, NextApiResponse } from "next";
import { nft } from "../../nft";

const generateMintSignature = async (
req: NextApiRequest,
res: NextApiResponse,
) => {
const { tokenId, address, tokenName } = JSON.parse(req.body);

const sdk = ThirdwebSDK.fromPrivateKey(
// Learn more about securely accessing your private key: https://portal.thirdweb.com/web3-sdk/set-up-the-sdk/securing-your-private-key
"<your-private-key-here>",
"mumbai",
);

const edition = sdk.getEdition("0x4ebCf39BCeEA3C9790605BF292B4a2b8811C93eD");

const getToken = (tokenName: string) => {
return nft.tokens.find((t) => t.name === tokenName);
};

const token = getToken(tokenName) ? getToken(tokenName) : getToken("MATIC");

try {
const signedPayload = await edition.signature.generateFromTokenId({
tokenId: tokenId,
quantity: "1",
to: address,
currencyAddress: token?.address,
price: token?.price,
});

res.status(200).json({
signedPayload: signedPayload,
});
} catch (err) {
console.log(err);
res.status(500).json({
error: err,
});
}
};

export default generateMintSignature;

Calling the api from the frontend

Let's now use the api in our frontend! Create a new function in index.tsx like this:

const mintWithSignature = async (tokenId: string) => {
const signedPayloadReq = await fetch(`/api/generate-mint-sig`, {
method: "POST",
body: JSON.stringify({ tokenName, tokenId, address }),
});

const signedPayload = (await signedPayloadReq.json()).signedPayload;

try {
const nft = await edition?.signature.mint(signedPayload);
return nft;
} catch (e) {
console.error(e);
return null;
}
};

We need to use the useEdition hook to access the edition contract here like this:

const edition = useEdition("0x4ebCf39BCeEA3C9790605BF292B4a2b8811C93eD");

We need to import this hook as well-

import { useAddress, useEdition, useMetamask } from "@thirdweb-dev/react";

Now, attach this function to the mint button-

<button onClick={() => mintWithSignature(nft.id)}>Mint</button>

If you try to mint the NFT now it works! 🎉

On the dashboard too, you would be able to see the supply increase.

NFT supply

Conclusion

Hope you were able to sell your NFT for various tokens! If you want to have a look at the code, check out this repo.