Publishing Price Levels

Publishing Price Levels

To receive RFQs, the Market Maker must publish Price Levels (or indicative quotes) to the API. These updates should be sent every second for each supported pair.

The published levels must include both the BUY and SELL sides within the same message. All prices are specified to the same market (e.g., ETH/USDC) for both directions.

Event: message

The message event with messageType "priceLevels" is used to communicate pricing levels for a specific market pair. This event provides details about the buy and sell orders available on the market.

priceLevels Message Schema

The priceLevels message schema ensures that the data structure sent through the WebSocket adheres to the required format. Below is the detailed schema and description for the priceLevels message.

{
  "messageType": "priceLevels",
  "message": {
    "protocols": ["pool"]
    "baseChain": {
      "chainType": "evm",
      "chainId": 1
    },
    "quoteChain": {
      "chainType": "evm",
      "chainId": 1
    },
    "baseToken": "0xBaseTokenAddress",
    "quoteToken": "0xQuoteTokenAddress",
    "buyLevels": [
      {
        "quantity": "10.0",
        "price": "100.0"
      }
    ],
    "sellLevels": [
      {
        "quantity": "5.0",
        "price": "110.0"
      }
    ]
  }
}

The priceLevels message contains the following fields:

Buy and Sell Levels Requirements

  • Buy Level: Indicates that the market maker is buying the baseToken, and the trader is selling it.

  • Sell Level: Indicates that the market maker is selling the baseToken, and the trader is buying it.

Levels are presented as an ordered list, showing the quantities available at specific prices, incrementally. The first level also represents the smallest quantity the market maker is willing to trade. If the market maker can accept arbitrarily small orders, the first level should have a quantity of 0 and the price of the next level.

  • To send price levels for all sources, set the source field to null or omit it entirely (this field is optional).

  • Submitting only one level will be rejected by the API.

  • Sending an empty list of levels for either BUY or SELL signifies that no trades are available in that direction. This approach is also recommended for gracefully disconnecting from the WebSocket.

Let's look at the following example for a ETH/USDC pair on Ethereum:

{
  baseToken: {
    chain: { chainType: 'evm', chainId: 1 },
    // We represent native ETH as 0x0.
    address: '0x0000000000000000000000000000000000000000', 
  },
  quoteToken: {
    chain: { chainType: 'evm', chainId: 1 },
    // USDC addrress on Ethereum
    address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
  },
  buyLevels: [
    { quantity: '0.1', price: '1600.00' },
    { quantity: '1', price: '1600.00' },
    { quantity: '0.5', price: '1599.00' }
  ],
  sellLevels: [
    { quantity: '0', price: '1601.00' },
    { quantity: '1', price: '1601.00' },
    { quantity: '1', price: '1602.00' }
  ]
}

Suppose the trader wanted to swap 1.2 ETH for USDC. In this case, the maker would be selling 0.1 ETH at 1600.00, another 1 ETH at 1600.00, and up to another 0.5 ETH at 1599.00. If the trader were to make an RFQ, they would get 0.1 * 1600.00 + 1 * 1600.00 + 0.1 * 1599.00, which is 1919.9 USDC

If the trader wanted to swap 0.05 ETH instead, the maker would not be able to honor the quote, as 0.1 is the minimum amount (first level).

On the other hand, the trader wanted to swap 2000 USDC for ETH. We know the market maker is selling 1 ETH for 1601.00, and up to another 1 ETH for 1602.00. The trader would spend 1601 USDC at the 1601.00 price point to get 1 ETH, and then another 399 USDC at the 1602.00 price to get 0.24906367041 ETH. Therefore, the RFQ would yield 1.24906367041 ETH.

In the case of swapping USDC for ETH, the trader could swap any small amount (e.g. 0.1 USDC), because the first level has a quantity of 0

Example: Connecting to the Socket and Publishing Price Levels

The code snippet below demonstrates how to connect to the socket server and publish price levels. You can adjust each price level according to your specific needs, such as the asset pair, price, and quantity.

const WebSocket = require("ws");

const path = ''
const jwtToken = ''
const ws = new WebSocket(path, {
  headers: { 'websocket-authorization': jwtToken }
});

function startSendingPriceLevels() {
  setInterval(() => {
    sendPriceLevels();
  }, 30000); // 30 seconds interval
}

function sendPriceLevels() {
  const priceLevelsMessage = {
    messageType: "priceLevels",
    message: {
      protocols: ["pool"],
      data: {
        baseChain: {
          chainType: "evm",
          chainId: 97
        },
        quoteChain: {
          chainType: "evm",
          chainId: 97
        },
        baseToken: "0xBaseTokenAddress",
        quoteToken: "0xQuoteTokenAddress",
        buyLevels: [
          {
            quantity: "1.0",
            price: "4.0"
          },
          {
            quantity: "2.0",
            price: "3.5"
          }
        ],
        sellLevels: [
          {
            quantity: "1.0",
            price: "4.5"
          },
          {
            quantity: "2.0",
            price: "5.0"
          }
        ]
      }
    }
  };

  ws.send(JSON.stringify(priceLevelsMessage));
  console.log("Sent priceLevels message to server");
}

ws.on('open', () => {
  startSendingPriceLevels();
});

ws.on("message", async (message) => {
  const request = JSON.parse(message);
  console.log(request)
  if (request.messageType === "request-signature") {
    const response = await processSignatureRequest(request);
    console.log("res ", JSON.stringify(response))
    ws.send(JSON.stringify(response));
  }
});

The vaultAddress parameter is used for integrating with the X Vault. If you do not wish to use the X Vault, you can omit or skip this parameter.

Last updated