Skip to main content
Time: ~60 minutes
Prerequisites: Completed the Build a Game tutorial
Build an in-game marketplace where players can buy, sell, and trade NFTs.
Sample Projects Available — See working marketplace implementations:

What We’re Building

  • Browse NFT listings
  • Display player inventory
  • Create sell listings
  • Purchase NFTs from other players
  • Cancel listings

Installation

npm install @imtbl/auth @imtbl/wallet @imtbl/orderbook @imtbl/blockchain-data @imtbl/config viem
Environment variables (.env.local):
NEXT_PUBLIC_CLIENT_ID=your_client_id
NEXT_PUBLIC_PUBLISHABLE_KEY=your_publishable_key
NEXT_PUBLIC_CONTRACT_ADDRESS=your_nft_contract
Get your Client ID and Publishable Key from Hub. See API Keys for details.

Display Player Inventory

Before listing NFTs for sale, players need to see what they own.
import { BlockchainData } from '@imtbl/blockchain-data';
import { Environment } from '@imtbl/config';

const indexer = new BlockchainData({
  baseConfig: { environment: Environment.SANDBOX },
});

async function loadInventory(address: string) {
  const { result } = await indexer.listNFTsByAccountAddress({
    chainName: 'imtbl-zkevm-testnet',
    accountAddress: address,
    contractAddress: CONTRACT_ADDRESS,
  });

  return result.map(nft => ({
    tokenId: nft.token_id,
    name: nft.name || `Item #${nft.token_id}`,
    image: nft.image || '/placeholder.png',
    attributes: nft.attributes,
  }));
}

Display Marketplace Listings

Show active listings that players can purchase.
import { Orderbook, OrderStatusName } from '@imtbl/orderbook';
import { Environment } from '@imtbl/config';
import { formatEther } from 'viem';

const orderbook = new Orderbook({
  baseConfig: {
    environment: Environment.SANDBOX,
    publishableKey: process.env.NEXT_PUBLIC_PUBLISHABLE_KEY!,
  },
});

async function loadListings() {
  const { result } = await orderbook.listListings({
    sellItemContractAddress: CONTRACT_ADDRESS,
    status: OrderStatusName.ACTIVE,
    pageSize: 50,
  });

  return result.map(listing => ({
    id: listing.id,
    tokenId: listing.sell[0].tokenId,
    name: listing.sell[0].name || `Item #${listing.sell[0].tokenId}`,
    image: listing.sell[0].image,
    price: formatEther(BigInt(listing.buy[0].amount)),
    seller: listing.accountAddress,
  }));
}

Create Listings

Let players list their NFTs for sale.
import { Orderbook } from '@imtbl/orderbook';
import { createWalletClient, custom, parseEther } from 'viem';
import { immutableZkEvmTestnet } from 'viem/chains';

async function createListing(tokenId: string, priceInIMX: string) {
  const provider = await getProvider();
  const [address] = await provider.request({ method: 'eth_accounts' });
  
  const walletClient = createWalletClient({
    chain: immutableZkEvmTestnet,
    transport: custom(provider),
  });

  // 1. Prepare the listing
  const { orderComponents, orderHash, typedData } = await orderbook.prepareListing({
    makerAddress: address,
    sell: {
      type: 'ERC721',
      contractAddress: CONTRACT_ADDRESS,
      tokenId: tokenId,
    },
    buy: {
      type: 'NATIVE',
      amount: parseEther(priceInIMX).toString(),
    },
  });

  // 2. Sign the listing (gasless)
  const signature = await walletClient.signTypedData({
    account: address,
    ...typedData,
  });

  // 3. Submit the listing
  const { result } = await orderbook.createListing({
    orderComponents,
    orderHash,
    orderSignature: signature,
    makerFees: [],
  });

  console.log('Listing created:', result.id);
  return result;
}

Fill Orders (Buy NFTs)

Let players purchase listed NFTs.
async function buyNFT(listingId: string) {
  const provider = await getProvider();
  const [address] = await provider.request({ method: 'eth_accounts' });
  
  const walletClient = createWalletClient({
    chain: immutableZkEvmTestnet,
    transport: custom(provider),
  });
  
  const publicClient = createPublicClient({
    chain: immutableZkEvmTestnet,
    transport: http(),
  });

  // 1. Prepare the fulfillment
  const { actions } = await orderbook.fulfillOrder(listingId, address, []);

  // 2. Execute all required transactions
  for (const action of actions) {
    if (action.type === 'TRANSACTION') {
      const hash = await walletClient.sendTransaction({
        to: action.to,
        data: action.data,
        value: BigInt(action.value || '0'),
      });
      await publicClient.waitForTransactionReceipt({ hash });
    }
  }

  console.log('Purchase complete!');
}

Cancel Listings

Let players remove their listings from the marketplace.
async function cancelListing(orderId: string) {
  const provider = await getProvider();
  const [address] = await provider.request({ method: 'eth_accounts' });
  
  // Option 1: Soft cancel (gasless, instant)
  await orderbook.cancelOrders([orderId], address);
  console.log('Listing cancelled (soft)');
  
  // Option 2: Hard cancel (on-chain, guaranteed)
  const walletClient = createWalletClient({
    chain: immutableZkEvmTestnet,
    transport: custom(provider),
  });
  
  const publicClient = createPublicClient({
    chain: immutableZkEvmTestnet,
    transport: http(),
  });

  const { signableAction } = await orderbook.prepareOrderCancellations([orderId]);
  
  const hash = await walletClient.sendTransaction({
    to: signableAction.to,
    data: signableAction.data,
  });
  
  await publicClient.waitForTransactionReceipt({ hash });
  console.log('Listing cancelled (on-chain)');
}

Testing Your Marketplace

  1. Start your project (TypeScript: npm run dev, Unity: Play Mode, Unreal: PIE)
  2. Login with Passport
  3. Create a listing - List one of your NFTs for sale
  4. Switch accounts - Use a different browser/incognito window
  5. Purchase the listing - Buy the NFT with the second account
  6. Verify the transfer - Check that the NFT moved to the buyer

Advanced Features

Once you have the basics working, consider adding:
Let buyers bid on any NFT in a collection. See Collection Bids.
Filter listings by price range. Use the minPrice and maxPrice params in listListings.
Filter by NFT traits (rarity, type). See Metadata Search.
Show recent sales using the Indexer’s activities endpoint.
Let players buy IMX with fiat. Available in Unity/Unreal via the Marketplace package.

Sample Projects

Explore complete marketplace implementations:

Next Steps