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.
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
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:
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:
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 order 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 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:
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 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 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.
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:
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.
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 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.
// 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.
- Typescript
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.