Create, cancel and fill listings
Learn how to utilize the orderbook SDK to create, cancel and fill listings.
Immutable's Orderbook APIs and SDKs make it simple to create and settle orders.
Setup
Prerequisites
Node Version 20 or later
To install nvm
follow these instructions. Once installed, run:
nvm install --lts
- (Optional) To enable Code Splitting (importing only the SDK modules you need) there are additional prerequisites.
Install the Immutable SDK
Run the following command in your project root directory.
- npm
- yarn
npm install -D @imtbl/sdk
# if necessary, install dependencies
npm install -D typescript ts-node
yarn add --dev @imtbl/sdk
# if necessary, install dependencies
yarn add --dev typescript ts-node
The Immutable SDK is still in early development. If experiencing complications, use the following commands to ensure the most recent release of the SDK is correctly installed:
- npm
- yarn
rm -Rf node_modules
npm cache clean --force
npm i
rm -Rf node_modules
yarn cache clean
yarn install
In order to get started, we'll first need to set up an Orderbook client:
- Typescript
import { config, orderbook } from '@imtbl/sdk';
import { ethers } from 'ethers'; // ethers v5
const PUBLISHABLE_KEY = 'YOUR_PUBLISHABLE_KEY'; // Replace with your Publishable Key from the Immutable Hub
const client = new orderbook.Orderbook({
baseConfig: {
environment: config.Environment.SANDBOX,
publishableKey: PUBLISHABLE_KEY,
},
});
Create listing
The Orderbook is responsible for managing the status of NFT orders. It does this by monitoring on-chain events and updating order statuses.
See the table below for a list of the order statuses:
Status | Description | Terminal (status cannot be reversed) |
---|---|---|
PENDING | This status is attached when an order has just been created and has not yet been validated. Additionally, another reason is that the listing time has not yet begun. | No |
ACTIVE | The order has passed all validation requirements and can be filled (purchased) by a user. A partially filled order will remain ACTIVE until it is fully filled. | No |
INACTIVE | The order does not have the required approval or the owner's balance was not sufficient to successfully list the order. Note that INACTIVE orders can become ACTIVE if conditions are met | No |
CANCELLED | The order has been cancelled and can no longer by filled. | Yes |
FILLED | The order has been filled (purchased) fully. | Yes |
EXPIRED | The order has expired and can no longer be filled. | Yes |
To create a listing, you can use the prepareListing
function provided by the Orderbook client, followed by createListing
function. It's important to note that listing creation does not incur any gas fees but approval does require small amount of gas. The order will need to go through various validations before it becomes fulfillable and transitions to the ACTIVE
state.
By adhering to royalty requirements and following the necessary validations, your listings will be ready for trading on the marketplace. Below is an example of how to create a listing:
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.
Prepare
First we need to prepare the listing. This will return an unsigned approval transaction if the user has not approved the settlement contract for the collection yet. The user will also need to sign the EIP712 order details.
// prepare ERC721 listing
const prepareERC721Listing =
async (): Promise<orderbook.PrepareListingResponse> => {
// build the sell item
const sell: ERC721Item = {
contractAddress: sellItemContractAddress,
tokenId: sellItemTokenID,
type: "ERC721",
};
// build the buy item
const buy =
buyItemType === "Native"
? ({
amount: buyItemAmount,
type: "NATIVE",
} as NativeItem)
: ({
amount: buyItemAmount,
type: "ERC20",
contractAddress: buyItemContractAddress,
} as ERC20Item);
// build the prepare listing parameters
const prepareListingParams: PrepareListingParams = {
makerAddress: accountsState[0],
buy,
sell,
};
// invoke the orderbook SDK to prepare the listing
return await orderbookSDK.prepareListing(prepareListingParams);
};
Create
To create the listing, the EIP712 signature needs to be sent to the orderbook.
export const createListing = async (
client: orderbook.Orderbook,
preparedListing: orderbook.PrepareListingResponse,
orderSignature: string,
makerEcosystemFee?: {
recipientAddress: string;
amount: string;
},
): Promise<string> => {
const order = await client.createListing({
orderComponents: preparedListing.orderComponents,
orderHash: preparedListing.orderHash,
orderSignature,
// Optional maker marketplace fee
makerFees: makerEcosystemFee ? [
{
recipientAddress: makerEcosystemFee.recipientAddress, // Replace address with your own marketplace address
amount: makerEcosystemFee.amount,
},
] : [],
});
return order.result.id;
};
Fill listing
The fullfilment function in the orderbook SDK returns two unsigned transactions. One for the approval and the other for the fulfillment of the listing. These need to be signed and submitted by the user's signer.
Approval transaction only need to be submitted for ERC20 tokens and where the current approved amount is not sufficient. Note also that filling an order will incur a gas fee.
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.
// Fulfill ERC721 listing
const fulfillERC721Listing = async (listingID: string) => {
const { actions } = await orderbookSDK.fulfillOrder(
listingID,
accountsState[0],
takerEcosystemFeeRecipient != "" ? [{
recipientAddress: takerEcosystemFeeRecipient, // Replace address with your own marketplace address
amount: takerEcosystemFeeAmount, // Insert taker ecosystem/marketplace fee here
}] : [],
);
for (const action of actions) {
if (action.type === orderbook.ActionType.TRANSACTION) {
const builtTx = await action.buildTransaction();
await (await signer.sendTransaction(builtTx)).wait(1);
}
}
};
After this transaction has been confirmed, the status of this order will change to FILLED
. You can poll Get orders to monitor this transition.
Stay tuned for bulk orders fulfillment capability within the orderbook SDK.
Cancel listing
Cancellation will transition any order state into CANCELLED
. This will render the order unfulfillable for the remainder of its time. Note also that order cancellations will incur a gas fee.
- Typescript
import { orderbook } from '@imtbl/sdk';
import { Signer } from 'ethers'; // ethers v5
import { TransactionResponse } from "@ethersproject/providers"; // ethers v5
const cancelListingOnChain = async (
client: orderbook.Orderbook,
signer: Signer,
listingId: string
): Promise<TransactionResponse> => {
const offerer = await signer.getAddress();
const { cancellationAction } = await client.cancelOrdersOnChain(
[ listingId ],
offerer
);
const unsignedCancelOrderTransaction = await cancellationAction.buildTransaction();
const receipt = await signer.sendTransaction(unsignedCancelOrderTransaction);
return receipt;
};
After this transaction has been confirmed, the status of this order will change to CANCELLED
. You can poll Get orders to monitor this transition.
Marketplace Bulk Action Limits
A single transaction has specific limits. If a request exceeds these limits, Immutable recommends breaking the requests into batches.
Bulk Action | Limit Per Transaction |
---|---|
Create Listings | 20 |
Fill Listing | 50 |
Soft Cancel | 20 |
Marketplace fees
One of the most attractive incentives that is unique to Immutable is Marketplace fees. Through our zkEVM Orderbook, any marketplace can attach fees to an order they facilitate. When these orders are filled on any marketplace on the protocol, the originating marketplace will receive the fees specified on the original listing.
There will be two types of marketplace fees on Immutable's Global Order Book:
Maker: A maker is a user who places an order on the marketplace that does not get executed immediately but instead rests on the order book, waiting for another user to take the other side of the trade. Makers add liquidity to the market by creating orders that can be executed in the future. The marketplace facilitating the placement of the original sell order would collect the
makerFees
Taker: A taker is a user who places an order that instantly matches with an existing order on the order book, effectively "taking" liquidity from the market. The marketplace facilitating the placement of the original sell order would collect the
takerFees
These two fees incentivise marketplaces to share liquidity with each other through Immutable's Global Order Book. If the marketplace creating the listing (maker) is also the marketplace that finds the buyer (taker); they wiill collect both fees if specified.
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.
Adding maker fees to new listings
To collect maker fees the marketplace must add a makerFees
when sending the locally signed order to the Immutable orderbook. This is done through the createListing
function call in Immutable's SDK. 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.
See the makerFees
section of the code example above in the Create Listing tutorial above.
For example, if the NFT is selling for 50 IMX, and a maker fee of 1% is applied, it should be represented like this:
makerFees: [{
amount: '500000000000000000', // 0.5 IMX
recipient: '0xFooBar', // Replace address with your own marketplace address
}]
Adding taker fees to trades
To collect taker fees the marketplace must add a taker fee when filling a listing on Immutable orderbook. This is done through the fulfillOrder
function call in Immutable's SDK. 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.
Refer to the comment below for the code section where a marketplace should input their taker fee amount when executing a listing. To fill an order see the example code block above in the Fill listing tutorial above.
For example, if the NFT is selling for 50 IMX, and a taker fee of 2% is applied, it should be represented like this:
const { actions, expiration, order } = await client.fulfillOrder(
listingId,
fulfiller,
[{
amount: '1000000000000000000', // Insert taker ecosystem/marketplace fee here
recipient: '0xFooBar', // Replace address with your own marketplace address
}]
);
Please also visit our Fees section for more information on Orderbook fees.