Skip to main content

Market orders go through the Just-In-Time (JIT) Auctions where Makers fight to fill orders before the order is allowed to fill against the Drift AMM.

JIT Maker Bots maintain a local copy of the decentralised orderbook (DLOB) (see: Keepers & Decentralised Orderbook) in order to see which orders are in the auction phase and can be bid on.

Getting Started

☠️*** This bot requires collateral to run. This tutorial is a developer's guide and holds no responsibility over bot outcomes.***

The reference implementation of the JIT Maker Bot is available here.

Follow the instructions at Keeper Bots to set the required environment variables, initialise theClearingHouseUser and deposit some collateral.

Start the JIT Maker Bot:

yarn run dev:jitmaker

Technical Explanation

Streaming User Orders

The Typescript SDK exposes an EventSubscriber object that you can use to receive market orders on-chain.

import { EventSubscriber, OrderRecord } from "@drift-labs/sdk";

// clearingHouse init omitted for brevity

const pollEventsConfig = {
type: "polling",
frequency: 1000,
};
const websocketEventsConfig = {
type: "websocket",
};

const eventSubscriber = new EventSubscriber(connection, clearingHouse.program, {
maxTx: 8192,
maxEventsPerType: 8192,
orderBy: "blockchain",
orderDir: "desc",
commitment: "confirmed",
logProviderConfig: pollEventsConfig,
});
eventSubscriber.subscribe();

eventSubscriber.eventEmitter.on("newEvent", async (event) => {
if (event.eventType === "OrderRecord") {
const order = event as OrderRecord;
// 1) update dlob
// 2) get orders from DLOB still in auction
// 3) bid on auctions
}
});

Bidding on JIT Auctions

Technical details on the JIT Auction and its pricing can be found at Just-In-Time (JIT) Auctions. The reference implementation acts on each user order received and makes the order in the opposite direction at the auction end price with a random order size between 20 and MAX_TRADE_SIZE_QUOTE.

// after finding a DLOB node still in the auction period
const jitMakerDirection = isVariant(nodeToFill.node.order.direction, "long")
? PositionDirection.SHORT
: PositionDirection.LONG;

// the auction start price will always be valid, but is not necessarily the best price
const jitMakerPrice = nodeToFill.node.order.auctionStartPrice;

// fill the entire order
const baseAmountToFill = nodeToFill.node.order.baseAssetAmount.sub(
nodeToFill.node.order.baseAssetAmountFilled
);

const txSig = await this.driftClient.placeAndMake(
{
orderType: OrderType.LIMIT,
marketIndex: nodeToFill.node.order.marketIndex,
baseAssetAmount: baseAmountToFill,
direction: jitMakerDirection,
price: jitMakerPrice,
postOnly: true,
immediateOrCancel: true,
},
{
taker: action.node.userAccount,
order: action.node.order,
}
);
console.log(`Bid on JIT auction: ${txSig}`);

Determine how much longer the auction will last

It may be helpful to determine how much time is left in the order's auction in order to get the current dutch auction price:

// determine how much auction time is left
const orderSlot = nodeToFill.node.order.slot.toNumber();
const currSlot = slotSubscriber.getSlot();
const aucDur = new BN(nodeToFill.node.order.auctionDuration);
const aucEnd = orderSlot + aucDur;

console.log(
`it has been ${currSlot - orderSlot} slots since order, auction ends in ${
aucEnd - currSlot
} slots`
);

Tracking Open Positions and Orders

The main ClearingHouse object from the SDK will update (polling or WebSocket) the user's account details behind the scenes. You can access the open orders and open positions of the user account by reading the positions and orders object of the ClearingHouseUser.

// you can force the sdk to fetch latest account data
await this.driftClient.fetchAccounts();
await this.driftClient.getUser().fetchAccounts();

const user = this.driftClient.getUser();

// check open positions
for (const p of user.getUserAccount().positions) {
if (p.baseAssetAmount.isZero()) {
// no open position in this index
continue;
}

console.log(p);
}

// check open orders
for (const o of user.getUserAccount().orders) {
if (isVariant(order.status, "init")) {
// no open order in this index
continue;
}
}