Getting Started with Serum Dex

Getting Started with Serum Dex

Start building on Serum DEX using Project Serum's all-new Developer Tooling Suite.

In this article, we will be looking at Project Serum's new Developer Tooling suite, serum-dev-tools, that comes with a new SDK, a CLI and as well as a Serum Explorer that provides you with more insight into various Serum DEX markets.

The purpose of this project is to create a developer tooling suite that significantly improves the developer experience of building projects composing with Serum DEX.

Now, let's look into the different tools one by one, shall we?

The CLI

The serum-dev-tools CLI can be installed into your machine with the following command,

cargo install serum-dev-tools

The CLI tool allows you to easily deploy a Serum DEX program into any Solana cluster you wish to do so. At the time of writing this article, it deploys the v3 version of the Serum DEX, which can be found here.

The first step to deploying is, initialising a workspace folder in whichever directory you're currently at. This is done by the following command,

serum-dev-tools init

// Output: Initialized dev-tools!

You'll now be able to see a folder by the name, dev-tools in your current directory. This folder will have,

1) The built serum-dex.so file, fetched from the Anchor registry.

2) A serum-dex-dev.json key-pair, which is used as the address at which the program will be deployed to.

You can print the public key for the above mentioned key-pair using the following command,

serum-dev-tools instance

// Output: 5bJT7CEMipCkEjmx6urgaJKP78x5Ag5ZEqbCb87UwdC4

Now that you have the binary file, and a key-pair for the program, it's time to deploy!

You can easily deploy the Serum Dex program to a cluster of your choice using the following command,

serum-dev-tools deploy localnet

// Output: Program Id: 5bJT7CEMipCkEjmx6urgaJKP78x5Ag5ZEqbCb87UwdC4

You have now, successfully deployed a Serum DEX v3 program!

Note: If you're looking to deploy to any other cluster apart from localnet, you will need a significant amount of SOL to do so everytime.

The SDK

Now that we have our program deployed, the next thing we wanna do is interact with it. This is where the new, @project-serum/serum-dev-tools SDK comes into play. It is available as a npm package here.

Using this package, you can easily create markets on a deployed Serum DEX program, place orders, cancel orders, and even run a market maker!

Persisting Keypairs

Since this package has a market making feature, which basically runs a market making script on a separate process, you will need to persist key-pairs between two or more different process. Due to this, serum-dev-tools provides FileKeypair, which is a wrapper class around @solana/web3.js's Keypair class, but provides additional functionality of saving those key-pairs in your local filesystem, allowing different processes to use them.

Here's how you would use the FileKeypair class,

import { FileKeypair } from "@project-serum/serum-dev-tools";

const owner = FileKeypair.generate("./scripts/keys/owner.json");
console.log("Owner: ", owner.keypair.publicKey.toString());

The owner key-pair shown above will now be used to make all the transactions interacting with the deployed Serum DEX program. But before we can do that, we need to fund this key-pair with some SOL.

import { Connection, LAMPORTS_PER_SOL } from "@solana/web3.js";

const connection = new Connection("http://localhost:8899", "confirmed");

const airdropSig = await connection.requestAirdrop(
    owner.keypair.publicKey,
    10 * LAMPORTS_PER_SOL,
);
await connection.confirmTransaction(airdropSig);

Initialize a Dex

To interact with the DEX program deployed, you will first need to initialize a Dex class. With an instance of this class, you can initialize a market, register coins, place orders, cancel orders, etc.

const dexAddress = new PublicKey(
    "5bJT7CEMipCkEjmx6urgaJKP78x5Ag5ZEqbCb87UwdC4",
  );
const dex = new Dex(dexAddress, connection);

Create Coins

The Dex class also stores a list of tokens, or Coins for which you can then create markets on the DEX. The markets created using an instance of Dex are also stored in a list of DexMarkets.

To create a Coin,

const baseCoin = await dex.createCoin(
    "SAYA",
    9,
    owner.keypair,
    owner.keypair,
    owner.keypair,
);
const quoteCoin = await dex.createCoin(
    "SRM",
    9,
    owner.keypair,
    owner.keypair,
    owner.keypair,
);

The createCoin method creates a Mint with the required parameters such as, decimals, mintAuthority, freezeAuthority, and also stores a symbol string as metadata to be able to fetch a Coin using it. It also registers this instance of Coin with the Dex by adding it to the list of Coins.

You can get a Coin that is registered with the Dex using the getCoin method as shown below,

const coin = dex.getCoin("SAYA");

You will also have to fund the owner key-pair with these coins before you can place orders and this is how that's done,

await baseCoin.fundAccount(2e6, owner.keypair, connection);
await quoteCoin.fundAccount(2e6, owner.keypair, connection);

Start a Market

We now have a Dex and a couple of Coins. It's time to create a market for these Coins on the Dex.

Here's how you do that,

const market = await dex.initDexMarket(owner.keypair, baseCoin, quoteCoin, {
    lotSize: 1e-3,
    tickSize: 1e-2,
});

The initDexMarket method takes in as parameters, a Keypair to be the payer, the Base Coin, the Quote Coin and two more parameters which are,

  • lotSize: This is the lowest representable quantity of the base coin. That would mean, if a market has a lotSize of 0.01, then the size of any order placed on this market must be an increment of 0.01.

  • tickSize: This is the lowest representable quantity of the quote coin. That would mean, if a market has a tickSize of 0.001 then the price of any order placed on this market must be an increment of 0.001.

In return, you get a DexMarket object which wraps @project-serum/serum's Market class along with metadata such as marketSymbol and the Coins for the market.

Place/Cancel Orders

Once you have a DexMarket, you're ready to place and cancel orders on the market. Here's how you place an order,

Note: All of the methods for DexMarket are static to allow using them in a separate node script, such as a market making script where you don't need properties such as baseCoin and quoteCoin, and only require using @project-serum/serum's Market.

const txSig = await DexMarket.placeOrder(
    connection,
    owner.keypair,
    market.serumMarket,
    "buy",
    "postOnly",
    10,
    10,
);

To be able to cancel orders, you first need to get which order you want to cancel. You can get all the orders for a key-pair using the following method,

const orders = await DexMarket.getOrdersForOwner(
    owner.keypair,
    market.serumMarket,
    connection
);

And now, to cancel one of these orders, you would do something like this,

const txSig = await DexMarket.cancelOrder(
    connection,
    owner.keypair,
    market.serumMarket,
    orders[0]
);

You can also get the Transaction object for placing and cancelling orders using the following methods,

const { placeTransaction, placeSigners } = await DexMarket.getPlaceOrderTransaction(
    connection,
    owner.keypair,
    market.serumMarket,
    "buy",
    "postOnly",
    10,
    10,
);

const { cancelTransaction, cancelSigners } = await DexMarket.getCancelOrderTransaction(
    connection,
    owner.keypair,
    serumMarket,
    orders[0],
);

We have now covered the basic features of serum-dev-tools, but we still have one last feature left to show!

Market Making

With serum-dev-tools, you can now run a market maker and test out your markets easily. What a market maker does is, it basically places multiple orders on the market with the sizes and prices spread across a range, in intervals.

Currently, the market making process we support is pretty basic, where we first fetch the latest price of an actual market from CoinGecko, and then place both bids and asks orders with prices spread on both sides. This allows your Serum market to behave like the actual market we referred to from CoinGecko.

For example, let's say the price returned from CoinGecko is p. Then, we would place 3 bids and 3 asks, and have the spread widen out by 1% for each successive order. The number of orders on each side here can be configured to any number as we'll see.

So, the bids prices would be 0.99 * p, 0.98 * p, and so on. Similarly, for asks, the prices would be 1.01 * p, 1.02 * p and so on.

In terms of sizing, the innermost orders should have the least size, increasing outward. You would have to specify an initialBidSize, which would be the size of the first bid placed.

The orders would look something like this,

  • Bid #1: Price = 0.99p, Size = initialBidSize * 1
  • Bid #2: Price = 0.98p, Size = initialBidSize * 2
  • Bid #3: Price = 0.97p, Size = initialBidSize * 3

  • Ask #1: Price = 1.01p, Size = (initialBidSize * 1) / p
  • Ask #2: Price = 1.02p, Size = (initialBidSize * 2) / p
  • Ask #3: Price = 1.03p, Size = (initialBidSize * 3) / p

We divide by p for the ask sizes so that the notional value of each successive ask is the same size as that of the corresponding bid.

After a specified interval, these orders are then cancelled and new orders with the latest price p are placed again. This process of placing and cancelling orders can go on for however long you wish for.

The following code shows how you can start a market maker using serum-dev-tools,

dex.runMarketMaker(market, owner, {
    durationInSecs: 15, // How long the market maker should run
    orderCount: 3, // How many orders on each side to be placed
    initialBidSize: 1000, // As explained above
    baseGeckoSymbol: "solana", // The base symbol used by CoinGecko for their API
    quoteGeckoSymbol: "usd", // The quote symbol used by CoinGecko for their API
});

This method, runs a market maker script on a separate node process. You can have a look at the script here.


That's it for today on Project Serum's new Developer Tooling Suite, serum-dev-tools. I hope you guys do check it out and let us know what you guys think! You can reach out to me on Twitter, @sayantanxyz

This project is in active development, and we have new features such as cranking, v4 support, and more configurations for market making lined up already. Feel free to report any bugs or issues here.