Skip to main content

Introduction

Order Matching Bots (Matching Bots) are responsible for matching two orders that cross or a taker order against the DAMM. Specifically, this includes:

  • Market Orders: Market Buy and Market Sell

  • Limit Orders: Limit Buy and Limit Sell

Matching Bots receive a small compensation for each order that they successfully fill.

See Keepers & Decentralised Orderbook for a technical explanation of how the decentralised orderbook (DLOB) and matching incentives work.

Matching Bots are similar to Tutorial: Order Trigger Bot in that they:

  • also maintain a local copy of the Decentralised Limit Orderbook (DLOB);

  • do not require the operator to manage collateral; and

  • receive a small reward for performing their duties.

Getting Started

The reference implementation of the Order Matching Bot is available here.

Follow the instructions at Keeper Bots to set the required environment variables and make sure a ClearingHouseUser is initialized.

Start the Matching Bot:

yarn run dev:filler

Technical Explanation

1. Get nodes from the DLOB that are ready to be filled

Market orders that are sent on the Drift Protocol first go through the Just-In-Time (JIT) Auctions. After the auction period, Matching Bots step in to fill orders for a small reward.

The DLOB implementation includes a method for getting orders ready to be filled:

const market = this.clearingHouse.getMarketAccounts()[0]; // get a MarketAccount

const oraclePriceData = this.driftClient.getOracleDataForMarket(marketIndex);
const oracleIsValid = isOracleValid(
market.amm,
oraclePriceData,
this.driftClient.getStateAccount().oracleGuardRails,
this.slotSubscriber.getSlot()
);

const vAsk = calculateAskPrice(market, oraclePriceData);
const vBid = calculateBidPrice(market, oraclePriceData);

const nodesToFill = this.dlob.findNodesToFill(
marketIndex,
vBid,
vAsk,
this.slotSubscriber.getSlot(),
oracleIsValid ? oraclePriceData : undefined
);

2. Filter for Fillable Nodes

To avoid trying to fill orders that aren't ready to be filled, filter out orders that are too small to fill

if (
!nodeToFill.makerNode &&
(isVariant(nodeToFill.node.order.orderType, "limit") ||
isVariant(nodeToFill.node.order.orderType, "triggerLimit"))
) {
const baseAssetAmountMarketCanExecute =
calculateBaseAssetAmountMarketCanExecute(
market,
nodeToFill.node.order,
oraclePriceData
);

if (
baseAssetAmountMarketCanExecute.lt(market.amm.baseAssetAmountStepSize)
) {
// skip order
continue;
}
}

3. Call fill_order on DriftClient

const user = this.userMap.get(nodeToFill.node.userAccount.toString());
const txSig = await this.driftClient.fillOrder(
nodeToFill.node.userAccount,
user.getUserAccount(),
nodeToFill.node.order,
undefined
);