Skip to main content

Create orders - listings

This page shows you could create a listing, so that you can list an NFT for sale on the Orderbook.


💡Listing vs Order

Immutable provides two distinct types of orders:

  1. Listings: These are orders initiated by an NFT owner who intends to sell their asset on a marketplace. Listings are considered sell orders.
  2. Bids: Representing a prospective buyer's intention to acquire an asset, bids allow users to express their interest in purchasing a specific asset. Users can place a bid on the order book, anticipating a match with a seller. Listings are considered buy orders.
It is important to note that while bids are not currently available on the zkEVM platform. However, their integration is a key part of Immutable's future development roadmap.

Prepare the listing

The call to prepareListing returns actions, order components and the order hash. The order components, order hash and a signature of from the signable action are required for creating the order in later steps. Actions for preparing a listing include both transaction and signable action types. The details of these actions are as follows:

  • APPROVAL transaction - An approval transaction is required when the user has not yet approved the seaport contract for the collection they are creating the listing for.
  • CREATE_LISTING signable message - This signable message is used to create the order on the Immutable order book.

Listing creation enforces royalties requirements based on the ERC2981 interface - prepare listing in the SDK will query the royalty information automatically.

The maker below is any ethers compatible Signer or Wallet instance for the user creating the listing.

note

Please be advised that all fees and quantities within our system are denoted in the smallest unit of the respective currency, and decimal representations are not supported.

For instance, IMX, which has 18 decimal places, will have a fee of 0.000000000000000001 IMX represented as "1".

Similarly, 1 IMX is represented as "1000000000000000000" in our system.

import { orderbook } from '@imtbl/sdk';
import { Wallet } from 'ethers'; // ethers v5

const prepareListing = async (
client: orderbook.Orderbook,
signer: Wallet,
): Promise<{preparedListing: orderbook.PrepareListingResponse, orderSignature: string}> => {
const offerer = await signer.getAddress();

const preparedListing = await client.prepareListing({
makerAddress: offerer,
// native payment token
buy: {
amount: '1000000',
type: 'NATIVE',
},
// ERC20 payment token
// buy: {
// amount: '1000000',
// type: 'ERC20',
// contractAddress: '0x5b0516606a8100342f6d45b24b8af8c4191cb172',
// },
sell: {
contractAddress: '0x300516606a8100342f6d45b24b8af8c4191cb195',
tokenId: '0',
type: 'ERC721',
},
});

let orderSignature = ''
for (const action of preparedListing.actions) {
// If the user hasn't yet approved the Immutable Seaport contract to transfer assets from this
// collection on their behalf they'll need to do so before they create an order
if (action.type === orderbook.ActionType.TRANSACTION) {
const builtTx = await action.buildTransaction()
console.log(`Submitting ${action.purpose} transaction`)
await signer.sendTransaction(builtTx);
}

// For an order to be created (and subsequently filled), Immutable needs a valid signature for the order data.
// This signature is stored off-chain and is later provided to any user wishing to fulfil the open order.
// The signature only allows the order to be fulfilled if it meets the conditions specified by the user that created the listing.
if (action.type === orderbook.ActionType.SIGNABLE) {
orderSignature = await signer._signTypedData(
action.message.domain,
action.message.types,
action.message.value,
)
}
}

return { preparedListing, orderSignature }
};

Sign and submit the approval transaction

If there is an approval transaction required for the listing, it needs to be signed and submitted to the zkEVM.

import { Web3Provider } from '@ethersproject/providers';
import { orderbook } from '@imtbl/sdk';

async function example(provider: Web3Provider, listing: orderbook.PrepareListingResponse) {
// get your user's Web3 wallet, e.g. MetaMask, Passport, etc
const signer = provider.getSigner();

const approvalAction = listing.actions.find(
(action): action is orderbook.TransactionAction =>
action.type === orderbook.ActionType.TRANSACTION
);

if (approvalAction) {
const unsignedTx = await approvalAction.buildTransaction();
const receipt = await signer.sendTransaction(unsignedTx);
await receipt.wait();
}
}

Sign the typed order data

For an order to be created (and subsequently filled), Immutable needs a valid signature for the order data. This signature is stored off-chain and is later provided to any user wishing to fulfil the open order. The signature only allows the order to be fulfilled if it meets the conditions specified by the user that created the listing.

import { Web3Provider } from '@ethersproject/providers';
import { orderbook } from '@imtbl/sdk';
import { Wallet } from 'ethers';

async function example(provider: Web3Provider, listing: orderbook.PrepareListingResponse) {
// get your user's Web3 wallet, e.g. MetaMask, Passport, etc
const signer = provider.getSigner();

const signableAction = listing.actions.find(
(action): action is orderbook.SignableAction =>
action.type === orderbook.ActionType.SIGNABLE
)!;

const signature = await signer._signTypedData(
signableAction.message.domain,
signableAction.message.types,
signableAction.message.value
);
}

Create the listing

This last step is sending the locally signed order to the Immutable orderbook where validation will be performed for the order. If the order contains malformed data, an incorrect signature or missing royalty information the server will return a invalid response, otherwise it will be server side signed and ready to be fulfilled.

When a marketplace submits a locally signed order to the Immutable orderbook, they should include a makerFees field as demonstrated in the code block below. This fee should be represented as the net amount that the marketplace wishes to receive for the services provided, and it should be quoted in the same ERC20 token in which the order is listed.

For example, if the NFT is selling for 50 IMX, and a maker fee of 1% is applied, it should be represented like this:

note

Please be advised that all fees and quantities within our system are denoted in the smallest unit of the respective currency, and decimal representations are not supported.

For instance, IMX, which has 18 decimal places, will have a fee of 0.000000000000000001 IMX represented as "1".

Similarly, 1 IMX is represented as "1000000000000000000" in our system.

makerFees: [{
amount: '500000000000000000', // 0.5 IMX
}]

For additional details on fees that ecosystems like marketplaces can incorporate into orders for the services they offer, please refer to our fee guide.

Orders created will initially be in PENDING status. Upon further validating blockchain approval events (if necessary) and balance checks (i.e. listing owner indeed owns NFT), it will become ACTIVE. You can read more about order statuses here.

💡Status polling
You can poll the Get Listing endpoint to check on status updates - in the near future we also plan on introducing push based (webhook) integration for order events.
import { orderbook } from '@imtbl/sdk';
import { Wallet, providers } from 'ethers'; // ethers v5

const createListing = async (
client: orderbook.Orderbook,
preparedListing: orderbook.PrepareListingResponse,
orderSignature: string
): Promise<void> => {
const order = await client.createListing({
orderComponents: preparedListing.orderComponents,
orderHash: preparedListing.orderHash,
orderSignature,
// Optional maker marketplace fee
makerFees: [{
amount: '100',
recipientAddress: '0xFooBar', // Replace address with your own marketplace address
}],
});
};