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.
Quick Overview
Creating a listing involves 4 steps:
- Prepare listing - SDK generates order components and typed data
- Sign & submit approval (if needed) - One-time per collection
- Sign typed data - User signs the listing (gasless)
- 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
});
| Duration | Use Case | Timestamp |
|---|
| 1 hour | Flash sales | Date.now() + 3600000 |
| 24 hours | Daily deals | Date.now() + 86400000 |
| 7 days | Standard listings | Date.now() + 604800000 |
| 30 days | Long-term listings | Date.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:
| Error | Cause | Solution |
|---|
INSUFFICIENT_BALANCE | User doesn’t own the NFT | Verify token ownership before listing |
INVALID_APPROVAL | Approval transaction failed | Retry approval transaction |
INVALID_SIGNATURE | Signature doesn’t match order | Ensure signing the correct typed data |
INVALID_BUY_AMOUNT | ERC-1155 buy amount not a multiple | Fix buy amount to be multiple of sell amount |
Next Steps