🔗
Token Integrations Guide
  • 🙂Introduction
    • Token Integrations
    • Token Gating vs Token Spending
    • Choosing the right numbers
  • 🚀App Store
    • Intro
    • Bonfire
      • Add your token
      • Create a P00LS Token Page
      • Create a Membership
    • Social Tipping
      • Farcaster Tipping
      • Lens Tipping
    • MadFi
    • Unlock
    • P00LS Customizable Embeds
      • Leaderboard Embeds
      • Reward Embeds
    • Lens App Integrations
      • How to add your token to the Lens allowlist
  • 📩Communication
    • Communicating with Token Holders
    • Spaces
      • Telegram
      • Discord
    • Gating Tools & Bots
      • Collabland
        • Collabland Telegram
        • Collabland Discord
      • Vulcan
        • Vulcan and Discord
      • Guild
        • Guild Telegram
        • Guild Discord
  • 📄Content
    • Content for Token Holders only
    • Bonfire
    • Shopify
  • 🔶NFTs
    • NFTs x P00LS Tokens
    • Coinvise
    • Sound
    • Thirdweb
      • Advanced Marketplace
  • 🎁Quests / Giveaways
    • Reward participation
    • Tropee
  • ❓Polls
    • Asking your Token Holders
    • Snapshot
      • Space Setup Guide
      • Conducting a Poll
Powered by GitBook
On this page
  • 1. Deploy a Marketplace smart contract
  • 2. Build your marketplace app
  • 3. Create NFT detail page
  • 4. Create Sell page
  1. NFTs
  2. Thirdweb

Advanced Marketplace

Build Your Own NFT Marketplace

PreviousThirdwebNextReward participation

Last updated 1 year ago

This guide will show you how to build an NFT Marketplace using , , & Thirdweb's smart contract.

Time to complete : 1 - 2 days (est)

By the end you’ll have an NFT Marketplace application where users can list NFTs for direct or auction sale and buy NFTs that are listed:

1. Deploy a Marketplace smart contract

The first step is to deploy a smart contract with all of functionality that an NFT Marketplace needs.

  • Showcasing all NFTs in a collection, and whether they’re on sale

  • Allowing users to buy, sell, list, & auction NFTs between each other

  • Maintaining control of royalty & platform fees for the marketplace owners

In ‘Advanced Configurations’ you can set a platform fee

Select the network/chain you want deploy your marketplace to (must be the same chain of the NFTs you want listed) then select ‘Deploy Now’.

Under the permissions tab you can set the marketplace to be for a specific collection.

2. Build your marketplace app

Create and setup thirdweb app

Using thirdweb’s CLI run npx thirdweb create app to create a new app. Name your project and for this guide we will use Next.js and Typescript .

Once done installing, open your project in your code editor. Open _app.tsx and configure the activeChain to the chain that you deployed your contract to.

const activeChain = "mumbai";

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

export default MyApp;

Next create a folder called const and add a files called addresses.ts . This file will hold any of the contract addresses used in our marketplace.

export const MARKETPLACE_ADDRESS = //YOUR MARKETPLACE CONTRACT ADDRESS;
export const NFT_COLLECTION_ADDRESS = //YOUR NFT COLLECTION CONTRACT ADDRESS;

Create navigation

export function Navbar() {
    const address = useAddress();

    return (
        <div>
            <div>
                <Link href='/'>
                    <p>Marketplace</p>
                </Link>
                <div>
                    <Link href='/buy'>
                        <p>Buy</p>
                    </Link>
                    <Link href='/sell'>
                        <p>Sell</p>
                    </Link>
                </div>
                <div>
                    <ConnectWallet/>
                    {address && (
                        <Link href={`/profile/${address}`}>
                            {/* Image of avatar */}
                        </Link>
                    )}
                </div>
            </div>
        </div>
    )
};

We’ll create a navigation for our marketplace that will have links to buy and sell pages along with a ConnectWallet for a user to connect their wallet. Using useAddress to check if a wallet is connected to the marketplace and storing it in an address variable. When a wallet is connected an avatar link will appear to link user to their profile.

Add the Navbar component to your _app.tsx right above your Components .

Create a Buy page

For our Buy page we will displaying a list of NFTs that are listed for sale. We will be doing something similar for the Sell and Profile pages, so we’ll create an NFTGrid component we can use on all pages. Let’s first create an NFTComponent component for each NFT being displayed and an component to show those NFTs.

Create NFTComponent

type Props = {
    nft: NFT;
};

export default function NFTComponent({ nft }: Props) {
    const  {contract: marketplace, isLoading: loadingMarketplace } = 
				useContract(MARKETPLACE_ADDRESS, "marketplace-v3");

    const { data: directListing, isLoading: loadingDirectListing } = 
        useValidDirectListings(marketplace, {
            tokenContract: NFT_COLLECTION_ADDRESS,
            tokenId: nft.metadata.id,
        });

    const { data: auctionListing, isLoading: loadingAuction} = 
        useValidEnglishAuctions(marketplace, {
            tokenContract: NFT_COLLECTION_ADDRESS,
            tokenId: nft.metadata.id,
        });

    return (
        <div>
            <div>
                <ThirdwebNftMedia 
									metadata={nft.metadata}
								/>
            </div>
            <p>Token ID #{nft.metadata.id}</p>
            <p>{nft.metadata.name}</p>

            <div>
                {loadingMarketplace || loadingDirectListing || loadingAuction ? (
                    <p>Loading...</p>
                ) : directListing && directListing[0] ? (
                    <div>
                        <div>
                            <p>Price</p>
                            <p>
														{`${directListing[0]?.currencyValuePerToken.displayValue} 
															${directListing[0]?.currencyValuePerToken.symbol}`}
														</p>
                        </div>
                    </div>
                ) : auctionListing && auctionListing[0] ? (
                    <div>
                        <div>
                            <p>Minimum Bid</p>
                            <p>
														{`${auctionListing[0]?.minimumBidCurrencyValue.displayValue} 
														${auctionListing[0]?.minimumBidCurrencyValue.symbol}`}
														</p>
                        </div>
                    </div>
                ) : (
                    <div>
                        <div>
                            <p>Price</p>
                            <p>Not Listed</p>
                        </div>
                    </div>
                )}
            </div>
        </div>
    )
};

NFTComponent will show an image of the NFT, token ID, name, and price of the listed NFT if listed as a direct listing or listed for auction. To get the listing or auction price we’ll first get our marketplace contract with useContract , then using useValidDirectListings and useValidEnglishAuctions .

Create NFTGrid

type Props = {
    isLoading: boolean;
    data: NFTType[] | undefined;
    overrideOnclickBehavior?: (nft: NFTType) => void;
    emptyText?: string;
};

export default function NFTGrid({
    isLoading,
    data,
    overrideOnclickBehavior,
    emptyText = "No NFTs found",
}: Props) {
    return (
        <div>
            {isLoading ? (
		            <p>Loading...</p>
            ) : data && data.length > 0 ? (
                data.map((nft) => 
                    !overrideOnclickBehavior ? (
                        <Link
                            href={`/token/${NFT_COLLECTION_ADDRESS}/${nft.metadata.id}`}
                            key={nft.metadata.id}
                        >
	                        <NFT nft={nft} />
                        </Link>
                    ) : (
                        <div
                            key={nft.metadata.id}
                            onClick={() => overrideOnclickBehavior(nft)}
                        >
                            <NFT nft={nft} />
                        </div>
                    ))
            ) : (
                <p>{emptyText}</p>
            )}
        </div>
        
    )
};

NFTGrid will display our NFTComponent and by default will link the NFT to a detail page or you can provide an overrideOnclickBehavior to modify it. If there is no NFT data provided to the grid it will provide an emptyText .

Create Buy page

export default function Buy() {
    const { contract } = useContract(NFT_COLLECTION_ADDRESS);
    const { data, isLoading } = useNFTs(contract);

    return (
        <div>
            <h1>Buy NFTs</h1>
            <p>Browse and buy NFTs from this collection.</p>
            <NFTGridisLoading={isLoading}data={data}emptyText={"No NFTs found"}/>
        </div>)
};

Provide the NFTGrid with the data of the NFTs from the NFT collection using useContract to get an instance of your contract and useNFTs to get the data of NFTs.

3. Create NFT detail page

The detail page will show the metadata of the NFT selected, including media, name, description, and traits. Depending if the NFT is listed for sale the option for a user to buy the direct listing or place an auction will be shown.

async function buyListing() {
  let txResult;

  //Add for auction section
  if (auctionListing?.[0]) {
      txResult = await marketplace?.englishAuctions.buyoutAuction(
          auctionListing[0].id
      );
  } else if (directListing?.[0]){
      txResult = await marketplace?.directListings.buyFromListing(
          directListing[0].id,
          1
      );
  } else {
      throw new Error("No listing found");
  }

  return txResult;
}

buyListing() will allow a user to purchase a direct listing or to purchase an auction at the buy out price.

async function createBidOffer() {
  let txResult;
  if(!bidValue) {
      return;
  }

  if (auctionListing?.[0]) {
      txResult = await marketplace?.englishAuctions.makeBid(
          auctionListing[0].id,
          bidValue
      );
  } else if (directListing?.[0]){
      txResult = await marketplace?.offers.makeOffer({
          assetContractAddress: NFT_COLLECTION_ADDRESS,
          tokenId: nft.metadata.id,
          totalPrice: bidValue,
      })
  } else {
      throw new Error("No listing found");
  }
  return txResult;
}

createBidOffer() will allow a user to place an auction on an auction listing or make an offer on a direct listing.

4. Create Sell page

Create SaleInfo component

We will have to set approval for the marketplace contract to transfer the NFTs on behalf of the owner if someone were to purchase the listed NFT.

async function checkAndProvideApproval() {
    const hasApproval = await nftCollection?.call(
        "isApprovedForAll",
        nft.owner,
        MARKETPLACE_ADDRESS
    );

    if (!hasApproval) {
        const txResult = await nftCollection?.call(
            "setApprovalForAll",
            MARKETPLACE_ADDRESS,
            true
        );

        if (txResult) {
            console.log("Approval provided");
        }
    }

    return true;
}

Create a form for a user to fill out the required information in order to display their NFT on the marketplace. We will give the option for a user to list their NFT as a direct sale or an auction sale and set the initial price. Create a handle submission function that will check approval with checkAndProvideApproval before listing the NFT for sale.

const { mutateAsync: createDirectListing } =
		useCreateDirectListing(marketplace);

const { register: registerDirect, handleSubmit: handleSubmitDirect } = useForm<DirectFormData>({
    defaultValues: {
        nftContractAddress: NFT_COLLECTION_ADDRESS,
        tokenId: nft.metadata.id,
        price: "0",
        startDate: new Date(),
        endDate: new Date(),
    },
});

async function handleSubmissionDirect(data: DirectFormData) {
    await checkAndProvideApproval();
    const txResult = await createDirectListing({
        assetContractAddress: data.nftContractAddress,
        tokenId: data.tokenId,
        pricePerToken: data.price,
        startTimestamp: new Date(data.startDate),
        endTimestamp: new Date(data.endDate),
    });

    return txResult;
}

Create a form to handle the data needed for Direct Listings. Using the useCreateDirectListing hook we call the function needed to create this listing using the data from the form.

const { mutateAsync: createAuctionListing } =
    useCreateAuctionListing(marketplace);

const { register: registerAuction, handleSubmit: handleSubmitAuction } =
useForm<AuctionFormData>({
  defaultValues: {
    nftContractAddress: NFT_COLLECTION_ADDRESS,
    tokenId: nft.metadata.id,
    startDate: new Date(),
    endDate: new Date(),
    floorPrice: "0",
    buyoutPrice: "0",
  },
});

async function handleSubmissionAuction(data: AuctionFormData) {
    await checkAndProvideApproval();
    const txResult = await createAuctionListing({
        assetContractAddress: data.nftContractAddress,
        tokenId: data.tokenId,
        buyoutBidAmount: data.buyoutPrice,
        minimumBidAmount: data.floorPrice,
        startTimestamp: new Date(data.startDate),
        endTimestamp: new Date(data.endDate),
    });

    return txResult;
}

Also create a form to handle the data needed for Auction Listings. Using the useCreateAuctionListing hook we call the function needed to create this listing using the data from the form.

Create a tab for Direct Listings and Auction Listings and create forms for user to provide information for the listing.

<Web3ButtoncontractAddress={MARKETPLACE_ADDRESS}action={async () => {
        await handleSubmitDirect(handleSubmissionDirect)();
    }}onSuccess={(txResult) => {
     	router.push(`/token/${NFT_COLLECTION_ADDRESS}/${nft.metadata.id}`);
    }}>Create Direct Listing</Web3Button>

Add a Web3Button to each tab that will use the marketplace contract and call either handleSubmitDirect or handleSubmissionAuction and direct the user to the selected NFTs detail page if the listing transaction is successful.

Create Sales page

Sales page will only display the NFTs that the connected wallet owns and allow the user to select the NFT from the NFTGrid and fill out the SaleInfo form to list NFT for Direct and Auction sale.

const { contract } = useContract(NFT_COLLECTION_ADDRESS);
const address = useAddress();
const { data, isLoading } = useOwnedNFTs(contract, address);

Get and instance of the contract and the connected wallet address. Using useOwnedNFTs you will get returned an array of the NFTs that the wallet owns from the collection.

const [selectedNFT, setSelectedNFT] = useState<NFTType>();

Create a state variable that will toggle the SaleInfo if a selected owned NFT is selected.

{!selectedNFT ? (
    <NFTGriddata={data}isLoading={isLoading}overrideOnclickBehavior={(nft) => {
            setSelectedNFT(nft);
        }}emptyText={"You don't own any NFTs yet from this collection."}/>) : (
    <div>
        <div>
            <div>
                <ThirdwebNftMediametadata={selectedNFT.metadata}width="100%"height="100%"/>
                <div>
                    <div>
                        <button
                            onClick={() => {
                                setSelectedNFT(undefined);
                            }}>X</button>
                    </div>
                    <h2>{selectedNFT.metadata.name}</h2>
                    <SaleInfonft={selectedNFT}/>
                </div>
            </div>
        </div>
    </div>)}

When an NFT is not selected we will display the NFTGrid components. When an NFT is selected then show the SaleInfo for that NFT.

Find our & hit the "Deploy Now" button on the top right:

Marketplace smart contract
🔶
Page cover image
Next.js
Typescript
M
arketplace
Example
Marketplace Contract
Configure + Deploy