Skip to main content
Learn how to create NFT listings for both ERC-721 and ERC-1155 tokens using the TypeScript SDK. This guide covers the complete flow from preparation to submission.
See Getting Started for prerequisites and installation.

Quick Overview

Creating a listing involves 4 steps:
  1. Prepare listing - SDK generates order components and typed data
  2. Sign & submit approval (if needed) - One-time per collection
  3. Sign typed data - User signs the listing (gasless)
  4. Create listing - Submit signed order to orderbook

Creating an ERC-721 Listing

ERC-721 tokens are unique (1-of-1). Each listing sells exactly one token.

Prepare the Listing

import { orderbook } from '@imtbl/sdk';

const listing = await sdk.prepareListing({
  makerAddress: userAddress,
  sell: {
    type: 'ERC721',
    contractAddress: '0x...', // NFT contract
    tokenId: '123',
    // amount is always 1 for ERC721, no need to specify
  },
  buy: {
    type: 'NATIVE', // or 'ERC20'
    amount: '1000000000000000000', // 1 IMX in wei
    // For ERC20:
    // contractAddress: '0x...',
  },
  makerFees: [
    {
      recipientAddress: marketplaceWallet,
      amount: '10000000000000000', // 0.01 IMX marketplace fee
    },
  ],
});

console.log({
  actions: listing.actions,           // Actions to execute
  orderComponents: listing.orderComponents, // Order data
  orderHash: listing.orderHash,       // Order ID
});
Order Type: ERC-721 listings automatically use FULL_RESTRICTED order type, meaning they cannot be partially filled. The entire NFT must be purchased at once.

Handle Approval Transaction

For the general approval pattern, see Overview: Approval Pattern. If this is the user’s first listing for this NFT collection, they must approve the Seaport contract:
for (const action of listing.actions) {
  if (action.type === orderbook.ActionType.TRANSACTION) {
    // This is the approval transaction
    const txHash = await signer.sendTransaction({
      to: action.to,
      data: action.data,
    });

    // Wait for confirmation
    await txHash.wait();
    console.log('Approval confirmed');
  }
}
One-time approval: Users only need to approve once per NFT collection. Subsequent listings for the same collection skip this step.Royalty Enforcement: Approving the Seaport contract enables enforced royalties. For more details, see the Operator Allowlist documentation.

Sign the Order

After approval, user signs the typed data (gasless):
// Find the signable action
const `signableAction` = listing.actions.find(
  (action) => action.type === orderbook.ActionType.SIGNABLE
);

if (`signableAction`.purpose === orderbook.SignablePurpose.CREATE_LISTING) {
  const signature = await signer._signTypedData(
    signableAction.message.domain,
    signableAction.message.types,
    signableAction.message.value
  );

  console.log('Order signed:', signature);
}

Submit the Listing

Finally, submit the signed order to the orderbook:
const { result } = await sdk.createListing({
  orderComponents: listing.orderComponents,
  orderHash: listing.orderHash,
  orderSignature: signature,
  makerFees: listing.makerFees,
});

console.log({
  id: result.id,                    // Order ID
  status: result.status.name,       // Initially 'PENDING'
  accountAddress: result.account_address,
});
Status Transitions: Listings start as PENDING while the orderbook validates balance and approval. They transition to ACTIVE asynchronously (usually within seconds). Build your UI to handle this delay optimistically.

Creating an ERC-1155 Listing

ERC-1155 tokens support multiple copies. Listings can sell any quantity and support partial fills.

Prepare the Listing

const listing = await sdk.prepareListing({
  makerAddress: userAddress,
  sell: {
    type: 'ERC1155',
    contractAddress: '0x...', // NFT contract
    tokenId: '456',
    amount: '10', // Selling 10 copies
  },
  buy: {
    type: 'NATIVE',
    amount: '5000000000000000000', // 5 IMX total (0.5 IMX per item)
    // Buy amount MUST be a multiple of sell amount!
  },
  makerFees: [
    {
      recipientAddress: marketplaceWallet,
      amount: '50000000000000000', // 0.05 IMX total fee
    },
  ],
});
Buy Amount Rule: For ERC-1155, the buy.amount must be a multiple of sell.amount.Example:
  • Selling 10 items
  • Buy amount can be: 10, 20, 30, etc. (enables 1, 2, 3+ items per fill)
  • Buy amount of 15 would be invalid (not a multiple of 10)
This enables partial fills: buyers can purchase 1, 2, 5, or all 10 items.
Order Type: ERC-1155 listings automatically use PARTIAL_RESTRICTED order type, enabling partial fulfillment. Buyers can purchase any quantity up to the listed amount.

Approval, Signing, and Submission

The approval, signing, and submission steps are identical to ERC-721:
// 1. Handle approval (if needed)
for (const action of listing.actions) {
  if (action.type === orderbook.ActionType.TRANSACTION) {
    const txHash = await signer.sendTransaction({
      to: action.to,
      data: action.data,
    });
    await txHash.wait();
  }
}

// 2. Sign typed data
const `signableAction` = listing.actions.find(
  (action) => action.type === orderbook.ActionType.SIGNABLE
);
const signature = await signer._signTypedData(
  `signableAction`.message.domain,
  `signableAction`.message.types,
  `signableAction`.message.value
);

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

Maker Fees

Maker fees are set at listing creation and cannot be changed without canceling and recreating the listing.
// Single marketplace fee
makerFees: [
  {
    recipientAddress: '0x...',
    amount: '10000000000000000', // 0.01 IMX flat fee
  },
]

// Multiple recipients (platform + affiliate)
makerFees: [
  {
    recipientAddress: platformWallet,
    amount: '10000000000000000', // 0.01 IMX to platform
  },
  {
    recipientAddress: affiliateWallet,
    amount: '5000000000000000', // 0.005 IMX to affiliate
  },
]
Fee Amount Units: Fees are specified in the smallest currency unit (wei for native tokens).
  • 1 IMX = 1000000000000000000 wei (18 decimals)
  • Use helper functions to calculate percentage-based fees

Order Expiration

Set when your listing should automatically expire:
const listing = await sdk.prepareListing({
  // ... other params
  orderExpiry: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days
});
DurationUse CaseTimestamp
1 hourFlash salesDate.now() + 3600000
24 hoursDaily dealsDate.now() + 86400000
7 daysStandard listingsDate.now() + 604800000
30 daysLong-term listingsDate.now() + 2592000000

Complete Example

Full working example combining all steps:
import { Orderbook } from '@imtbl/orderbook';
import { Environment } from '@imtbl/config';
import { ethers } from 'ethers';

async function createNFTListing() {
  // Initialize SDK
  <SdkInit />

  // Get user's wallet
  const provider = new ethers.providers.Web3Provider(window.ethereum);
  const signer = provider.getSigner();
  const `userAddress` = await signer.getAddress();

  try {
    // 1. Prepare listing
    const listing = await sdk.prepareListing({
      makerAddress: `userAddress`,
      sell: {
        type: 'ERC721',
        contractAddress: '0x1234...', // Your NFT contract
        tokenId: '123',
      },
      buy: {
        type: 'NATIVE',
        amount: '1000000000000000000', // 1 IMX
      },
      makerFees: [{
        `recipientAddress`: '0x5678...', // Marketplace fee wallet
        amount: '10000000000000000', // 0.01 IMX
      }],
      `orderExpiry`: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days
    });

    // 2. Handle approval if needed
    for (const action of listing.actions) {
      if (action.type === orderbook.ActionType.TRANSACTION) {
        console.log('Requesting approval...');
        const tx = await signer.sendTransaction({
          to: action.to,
          data: action.data,
        });
        await tx.wait();
        console.log('Approved!');
      }
    }

    // 3. Sign the order
    const signableAction = listing.actions.find(
      (action) => action.type === orderbook.ActionType.SIGNABLE
    );
    const signature = await signer._signTypedData(
      signableAction.message.domain,
      signableAction.message.types,
      signableAction.message.value
    );

    // 4. Submit listing
    const { result } = await sdk.createListing({
      orderComponents: listing.orderComponents,
      orderHash: listing.orderHash,
      orderSignature: signature,
      makerFees: listing.makerFees,
    });

    console.log('Listing created!', {
      id: result.id,
      status: result.status.name,
    });

    return result;
  } catch (error) {
    console.error('Failed to create listing:', error);
    throw error;
  }
}

Error Handling

Common errors and solutions:
ErrorCauseSolution
INSUFFICIENT_BALANCEUser doesn’t own the NFTVerify token ownership before listing
INVALID_APPROVALApproval transaction failedRetry approval transaction
INVALID_SIGNATURESignature doesn’t match orderEnsure signing the correct typed data
INVALID_BUY_AMOUNTERC-1155 buy amount not a multipleFix buy amount to be multiple of sell amount

Next Steps

Order Management

Query and manage your listings

Fill Orders

Learn how buyers purchase your listings

Cancel Orders

Understand soft and hard cancellation

Bulk Operations

Create multiple listings in one transaction