Complete guide to fulfilling NFT orders (buying) using the Immutable Orderbook SDK
Learn how to fulfill (buy) NFT listings on the Immutable Orderbook. This guide covers filling both ERC-721 and ERC-1155 orders, including partial fills and fee handling.
The fulfillOrder() call returns actions that must be executed in order:
Action Type
Purpose
When Needed
APPROVAL
Approve Seaport to spend ERC-20 tokens
First time using that currency, or insufficient allowance
FULFILL_ORDER
Execute the trade
Always
const { actions, order, expiration } = await sdk.fulfillOrder( orderId, `takerAddress`, `takerFees` // optional);console.log({ actions: actions, // Actions to execute order: order, // Order with CURRENT fees (may differ from query) expiration: expiration, // Transaction expires in 3 minutes});
3-Minute Expiration: Transaction data expires 3 minutes after generation. If the user doesn’t submit within this window, you must call fulfillOrder() again to get fresh data with re-validated fees.
Fee Validation: The order object returned contains the most current fees, which may differ from when you queried the listing (e.g., protocol fee reductions during promotions). Always display the fees from this response to users before they sign.
Taker Fee Flexibility: Taker fees can be different for each fulfillment. Unlike maker fees (set at listing creation), marketplaces can set custom taker fees per transaction.
Collection bids and trait bids are criteria-style buy orders: the seller (taker) must tell the orderbook which tokenId they are selling into the bid. In the Orderbook SDK, pass tokenId as the fifth argument to fulfillOrder (after orderId, takerAddress, takerFees, and amountToFill). For a typical ERC-721 fill, set amountToFill to undefined and supply tokenId as a string.
Collection bid: any token from the collection can be used (subject to order rules). See Collection bids.
Trait bid: the same tokenId requirement applies, and the token’s indexed metadata must match every trait filter on the bid. If metadata is missing or does not match, fulfillment will fail—treat as a validation error and choose another asset or refresh metadata. See Trait bids.
For ERC-1155 orders, taker fees have special rules:
Critical: Taker Fee for Full OrderThe taker fee amount must always reflect the COMPLETE order, even for partial fills. The orderbook automatically pro-rates the fee based on quantity executed.Example:
// Listing: 10 items at 1 IMX each = 10 IMX total// Marketplace wants 1% fee// ❌ WRONG for partial fill (3 items):takerFees: [{ amount: '30000000000000000', // 0.03 IMX (scaled for 3 items) - INCORRECT}]// CORRECT for any fill (3, 5, or 10 items):takerFees: [{ amount: '100000000000000000', // 0.1 IMX (1% of 10 IMX total) - CORRECT}]// Orderbook will charge: 0.03 IMX for 3 items, 0.05 IMX for 5 items, etc.
For ERC-20 currency listings, buyers must approve Seaport to spend tokens:
const { actions } = await sdk.fulfillOrder(orderId, buyerAddress, []);for (const action of actions) { if (action.type === orderbook.ActionType.TRANSACTION) { if (action.purpose === orderbook.TransactionPurpose.APPROVAL) { console.log('Requesting ERC-20 approval...'); // User approves Seaport to spend their ERC-20 tokens } else if (action.purpose === orderbook.TransactionPurpose.FULFILL_ORDER) { console.log('Executing purchase...'); // The actual buy transaction } const unsignedTx = await action.buildTransaction(); const txResponse = await signer.sendTransaction(unsignedTx); await txResponse.wait(); }}
One-Time Approval: Users only need to approve each ERC-20 currency once (or when allowance is insufficient). Subsequent purchases with the same currency skip the approval step.Native Currency: Listings priced in NATIVE (IMX) never require approval—only a fulfillment transaction.For architectural context, see the Approval Pattern documentation.
Status Transitions: Orders transition from ACTIVE → FILLED asynchronously after the transaction confirms. For partial ERC-1155 fills, status remains ACTIVE until completely filled.