Skip to main content
Place bids on NFTs in a collection whose metadata matches filters you define (for example, Background is Blue or Red). Trait bids are a stricter form of collection bids. The seller must still choose a concrete token when filling, but that token’s indexed attributes must satisfy all of your trait criteria.
See Getting Started for prerequisites and installation.

What is a trait bid?

  • You offer ERC-20 tokens to buy one or more NFTs from a collection, identified by an ERC-721 collection or ERC-1155 collection buy item.
  • You attach traitCriteria: an array of filters. Each filter has a trait type (metadata attribute name) and a list of allowed values.
  • Matching semantics: every trait type you specify must be matched by the NFT’s metadata. For a given type, the token matches if its attribute value is one of the values you listed. Matching is case-insensitive for values and numerical values are converted to their string equivalent.
  • Orders appear as TRAIT_BID in API responses alongside listings, bids, and collection bids. Use the order type field when branching in your app or webhooks.

Prerequisites

  • Same setup as other orderbook flows (Passport or wallet, fees, etc.).
  • Metadata for fulfillment: when a seller fills your bid with a specific tokenId, Immutable validates that token’s attributes against your criteria. That requires the NFT’s metadata to be available through Immutable’s indexer. If metadata is missing or out of date, fulfillment can fail even if the token exists on-chain.

Creating a trait bid

The flow mirrors collection bids: prepare (builds orderComponents, orderHash, and actions) → run any approval transactions → sign the EIP-712 order from the SIGNABLE action → create.
import { Orderbook, ActionType } from '@imtbl/orderbook';

const prepared = await orderbook.prepareTraitBid({
  makerAddress: address,
  buy: {
    type: 'ERC721_COLLECTION',
    contractAddress: NFT_CONTRACT,
    amount: '1',
  },
  sell: {
    type: 'ERC20',
    contractAddress: PAYMENT_TOKEN,
    amount: '1000000000000000000', // wei
  },
});

// 1) Submit approval txs from prepared.actions (if any)
for (const action of prepared.actions) {
  if (action.type === ActionType.TRANSACTION) {
    const tx = await action.buildTransaction();
    await walletClient.sendTransaction(tx);
  }
}

// 2) Sign the CREATE_ORDER payload from the SIGNABLE action (EIP-712; same shape as collection bids)
const signable = prepared.actions.find((a) => a.type === ActionType.SIGNABLE);
if (!signable) throw new Error('Missing signable order action');

const orderSignature = await walletClient.signTypedData({
  account: address,
  domain: signable.message.domain,
  types: signable.message.types,
  primaryType: 'OrderComponents',
  message: signable.message.value,
});

// 3) Create the trait bid on Immutable
const { result } = await orderbook.createTraitBid({
  orderComponents: prepared.orderComponents,
  orderHash: prepared.orderHash,
  orderSignature,
  makerFees: [],
  // The NFT used for fulfillment must have:
  //   - A `Background` trait, with a value of eitehr `Blue` or `Red`
  //   - A `Rarity` trait, with a value of `Legendary`
  traitCriteria: [
    { traitType: 'Background', values: ['Blue', 'Red'] },
    { traitType: 'Rarity', values: ['Legendary'] },
  ],
});

console.log('Trait bid created:', result.id);

Filling a trait bid (seller)

Sellers call fulfillOrder with the trait bid order id and the token ID they are selling. The fifth argument is tokenId (criteria fulfillment); pass undefined for amountToFill when you are not doing a partial ERC-1155 quantity fill.
import { Orderbook } from '@imtbl/orderbook';

const { actions } = await orderbook.fulfillOrder(
  traitBidId,
  sellerAddress,
  [], // taker fees
  undefined, // amountToFill — unused for standard ERC-721 trait fill
  '123', // tokenId — required: which NFT the seller is selling into the bid
);

// Execute actions (approvals + fulfill) in order — same as filling listings
for (const action of actions) {
  if (action.type === 'TRANSACTION') {
    const unsignedTx = await action.buildTransaction();
    const hash = await walletClient.sendTransaction(unsignedTx);
    await publicClient.waitForTransactionReceipt({ hash });
  }
}
If the token’s indexed metadata does not satisfy every trait filter on the bid, the orderbook will not return valid fulfillment data for that token.
For more context on actions, fees, and expiry, see Fill orders.

Querying trait bids

List trait bids

Filter by collection contract, status, maker, and pagination — same ergonomics as listCollectionBids.
import { Orderbook, OrderStatusName } from '@imtbl/orderbook';

const { result, page } = await orderbook.listTraitBids({
  buyItemContractAddress: NFT_CONTRACT,
  status: OrderStatusName.ACTIVE,
  pageSize: 50,
});

for (const bid of result) {
  console.log(bid.id, bid.traitCriteria, bid.sell.amount);
}

Get one trait bid

const { result: bid } = await orderbook.getTraitBid(traitBidId);

console.log({
  id: bid.id,
  status: bid.status,
  traitCriteria: bid.traitCriteria,
  sell: bid.sell,
  buy: bid.buy,
});

Comparison: token bid vs collection bid vs trait bid

AspectToken bidCollection bidTrait bid
Buy targetOne specific tokenIdAny token in the collectionAny token in the collection whose metadata matches traitCriteria
FulfillmentSeller sells that tokenSeller passes tokenIdSeller passes tokenId + metadata must match
Typical useTarget a rare IDFloor sweep / any itemOffers on filtered sets (e.g. legendary + blue background)

Fees and cancellation

  • Fees: same maker/taker concepts as other orders — see Fees.
  • Cancel: use the bid’s order id with Cancel orders (soft or hard cancel patterns apply to open orders).

Next steps

Collection bids

Bids on any NFT in a collection without trait filters

Fill orders

Fulfillment, actions, and approvals

Order management

Status, pagination, and webhooks

Cancel orders

Cancel trait bids you created