Skip to main content

Create, cancel and fill bids

Learn how to utilize the orderbook SDK to create, cancel and fill bids.


Immutable's Orderbook APIs and SDKs make it simple to create and fill bids.

💡One settlement contract
On Immutable zkEVM, there is currently only one settlement contract for NFT trades. Only orders with royalties that adhere to ERC2981 standard will be approved by the Immutable operator and accepted by the settlement contract. More information on this is available in Introduction.

Bids Overview

There are two types of bids: bids and collection bids. Bids are placed against a specific token ID in a collection, while collection bids are used to bid on any asset in a collection.

Setup

Prerequisites

Node Version 20 or later
Immutable's Typescript SDK requires **Node v20** (Active LTS version) or **higher**. Node v20 can be installed via `nvm`.

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 install -D @imtbl/sdk
# if necessary, install dependencies
npm install -D typescript ts-node
Troubleshooting

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:

rm -Rf node_modules
npm cache clean --force
npm i

In order to get started, we'll first need to set up an Orderbook client:

Iniatialize Orderbook ClientView on GitHub
import { orderbook } from "@imtbl/sdk";
import { Environment } from "@imtbl/sdk/config";

export const orderbookSDK = new orderbook.Orderbook({
baseConfig: {
environment: Environment.SANDBOX,
},
});

Create bid

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:

StatusDescriptionTerminal (status cannot be reversed)
PENDINGThis status is attached when an order has just been created and has not yet been validated. Additionally, another reason is that the order time has not yet begun.No
ACTIVEThe 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
INACTIVEThe 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 metNo
CANCELLEDThe order has been cancelled and can no longer by filled.Yes
FILLEDThe order has been filled (purchased) fully.Yes
EXPIREDThe order has expired and can no longer be filled.Yes

To create a bid, you can use the prepareBid function provided by the Orderbook client, followed by createBid function. It's important to note that bid creation does not incur any gas fees but approval does require a 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 bids will be ready for trading on the marketplace. Below is an example of how to create a bid:

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.

Prepare

First we need to prepare the bid. 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 BidView on GitHub
// prepare ERC721 bid
const prepareERC721Bid =
async (): Promise<orderbook.PrepareBidResponse> => {
// build the sell item
const sell: ERC20Item = {
type: "ERC20",
contractAddress: sellItemContractAddress,
amount: sellItemAmount,
};

// build the buy item
const buy: ERC721Item = {
type: "ERC721",
contractAddress: buyItemContractAddress,
tokenId: buyItemTokenID,
};

// build the prepare bid parameters
const prepareBidParams: PrepareBidParams = {
makerAddress: accountsState[0],
buy,
sell,
};

// invoke the orderbook SDK to prepare the bid
return await orderbookSDK.prepareBid(prepareBidParams);
};

Creating a collection bid is very similar

Prepare ERC721 Collection BidView on GitHub
// prepare ERC721 collection bid
const prepareERC721CollectionBid =
async (): Promise<orderbook.PrepareCollectionBidResponse> => {
// build the sell item
const sell: ERC20Item = {
type: "ERC20",
contractAddress: sellItemContractAddress,
amount: sellItemAmount,
};

// build the buy item
const buy: ERC721CollectionItem = {
type: "ERC721_COLLECTION",
contractAddress: buyItemContractAddress,
amount: buyItemTokenAmount,
};

// build the prepare collection bid parameters
const prepareCollectionBidParams: PrepareCollectionBidParams = {
makerAddress: accountsState[0],
buy,
sell,
};

// invoke the orderbook SDK to prepare the collection bid
return await orderbookSDK.prepareCollectionBid(prepareCollectionBidParams);
};

Create

To create the bid, the EIP712 signature needs to be sent to the orderbook.

Create BidView on GitHub
export const createBid = async (
client: orderbook.Orderbook,
preparedBid: orderbook.PrepareBidResponse,
orderSignature: string,
makerEcosystemFee?: {
recipientAddress: string;
amount: string;
},
): Promise<string> => {
const order = await client.createBid({
orderComponents: preparedBid.orderComponents,
orderHash: preparedBid.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;
};

And the same example for collection bids:

Create Collection BidView on GitHub
export const createCollectionBid = async (
client: orderbook.Orderbook,
preparedBid: orderbook.PrepareBidResponse,
orderSignature: string,
makerEcosystemFee?: {
recipientAddress: string;
amount: string;
},
): Promise<string> => {
const order = await client.createCollectionBid({
orderComponents: preparedBid.orderComponents,
orderHash: preparedBid.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 Bid

The fullfilment function in the orderbook SDK returns multiple unsigned transactions. One or more approval transactions, and another for the fulfillment of the bid. These need to be signed and submitted by the user's signer.

Approval transactions only apply if the user filling the bid has not approved the setllement contract for the collection, or for the ERC20 being received.

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.

Fill ERC721 BidView on GitHub
// Fulfill ERC721 bid
const fulfillERC721Bid = async (bidID: string) => {
const { actions } = await orderbookSDK.fulfillOrder(
bidID,
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);
}
}
};

When filling a collection bid, the token ID being filled needs to also be passed to the fulfillOrder method.

Fill ERC721 Collection BidView on GitHub
// FulFill ERC721 Collection Bid
const fulfillERC721CollectionBid = async (
collectionBidID: string,
tokenID: string
) => {
const { actions } = await orderbookSDK.fulfillOrder(
collectionBidID,
accountsState[0],
takerEcosystemFeeRecipient != "" ? [{
recipientAddress: takerEcosystemFeeRecipient, // Replace address with your own marketplace address
amount: takerEcosystemFeeAmount, // Insert taker ecosystem/marketplace fee here
}] : [],
'1',
tokenID,
);

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.

Cancel bid

Cancellation will transition any order state into CANCELLED. This will render the order unfulfillable for the remainder of its time. Cancelling bids is identical to cancelling listings, just provide the bid ID instead of the listing ID in the example below.

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

const cancelListings = async (
client: orderbook.Orderbook,
signer: Wallet,
listingIds: string[]
) => {
const account = await signer.getAddress();

const { signableAction } = await client.prepareOrderCancellations(listingIds);
const cancellationSignature = await signer._signTypedData(
signableAction.message.domain,
signableAction.message.types,
signableAction.message.value,
)

return client.cancelOrders(listingIds, account, cancellationSignature)
};

The status of this order will transition to CANCELLED if it was not otherwise filled while the cancellation request was in flight. You can poll Get orders to monitor this transition.