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 { config, orderbook } from '@imtbl/sdk';
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