@microwavedcola/mango-vial v0.0.1
Credits
This project is forked from https://github.com/tardis-dev/serum-vial
mango-vial: real-time WS market data API for Mango Perp Markets
Getting started
Start a local instance
npm run start:debugRun the code snippet below in the browser Dev Tools directly or in Node.js (requires installation of ws lib.
const ws = new WebSocket('ws://localhost:8000/v1/ws')
ws.onmessage = (message) => {
  console.log(JSON.parse(message.data))
}
ws.onopen = () => {
  const subscribeL2 = {
    op: 'subscribe',
    channel: 'level2',
    markets: ['BTC-PERP']
  }
  ws.send(JSON.stringify(subscribeL2))
}Since by default mango-vial uses confirmed commitment level for getting accounts notification from RPC node, it may sometimes feel slightly lagged when it comes to order book updates vs default DEX UI which uses recent/processed commitment.
Installation
IMPORTANT NOTE
For the best mango-vial data reliability it's advised to set up a dedicated Solana RPC node and connect mango-vial to it instead of default https://solana-api.projectserum.com which may rate limit or frequently restart Websocket RPC connections since it's a public node used by many.
npx (requires Node.js >= 15 and git installed on host machine)
Installs and starts mango-vial server running on port 8000.
npx mango-vialIf you'd like to switch to different Solana RPC node endpoint like for example local one, change port or run with debug logs enabled, just add one of the available CLI options.
npx mango-vial --endpoint http://localhost:8090 --ws-endpoint-port 8899 --log-level debug --port 8900Alternatively you can install mango-vial globally.
npm install -g mango-vial
mango-vialCLI options
| name | default | description | 
|---|---|---|
| port | 8000 | Port to bind server on | 
| endpoint | https://solana-api.projectserum.com | Solana RPC node endpoint that mango-vial uses as a data source | 
| ws-endpoint-port | - | Optional Solana RPC WS node endpoint port that mango-vial uses as a data source (if different than REST endpoint port) source | 
| log-level | info | Log level, available options: debug, info, warn and error | 
| minions-count | 1 | Minions worker threads count that are responsible for broadcasting normalized WS messages to connected clients | 
| commitment | confirmed | Solana commitment level to use when communicating with RPC node, available options: confirmed and processed | 
| markets-json | @project-serum/serummarkets.json file, but only non depreciated markets | path to custom market.json definition file if one wants to run mango-vial for custom markets | 
Run npx mango-vial --help to see all available startup options.
Docker
Pulls and runs latest version of microwavedcola/mango-vial Docker Image on port 8000.
docker run -p 8000:8000 -d microwavedcola/mango-vial:latestIf you'd like to switch to different Solana RPC node endpoint, change port or run with debug logs enabled, just specify those via one of the available env variables.
docker run -p 8000:8000 -e "SV_LOG_LEVEL=debug" -d microwavedcola/mango-vial:latestENV Variables
| name | default | description | 
|---|---|---|
| SV_PORT | 8000 | Port to bind server on | 
| SV_ENDPOINT | https://solana-api.projectserum.com | Solana RPC node endpoint that mango-vial uses as a data source | 
| SV_WS_ENDPOINT_PORT | - | Optional Solana RPC WS node endpoint port that mango-vial uses as a data source (if different than REST endpoint port) source | 
| SV_LOG_LEVEL | info | Log level, available options: debug, info, warn and error | 
| SV_MINIONS_COUNT | 1 | Minions worker threads count that are responsible for broadcasting normalized WS messages to connected clients | 
| SV_COMMITMENT | confirmed | Solana commitment level to use when communicating with RPC node, available options: confirmed and processed | 
| SV_MARKETS_JSON | @project-serum/serummarkets.json file, but only non depreciated markets | path to custom market.json definition file if one wants to run mango-vial for custom markets | 
SSL/TLS Support
mango-vial supports SSL/TLS but it's not enabled by default. In order to enable it you need to set CERT_FILE_NAME env var pointing to the certificate file and KEY_FILE_NAME pointing to private key of that certificate.
WebSocket API
WebSocket API provides real-time market data feeds of Serum DEX and uses a bidirectional protocol which encodes all messages as JSON objects.
- each WebSocket client is required to actively send native WebSocket pings to the server with interval less than 30 seconds, otherwise connection may be dropped due to inactivity 
- message compression is enabled for clients supporting - permessage-deflate
Endpoint URL
- ws://localhost:8000/v1/ws - assuming mango-vial runs locally on default port without SSL enabled 
- wss://api.mango-vial.dev/v1/ws - demo mango-vial server endpoint 
Subscribing to data feeds
To begin receiving real-time market data feed messages, you must first send a subscribe message to the server indicating channels and markets for which you want the data for.
If you want to unsubscribe from channel and markets, send an unsubscribe message. The structure is equivalent to subscribe messages except op field which should be set to "op": "unsubscribe".
Subscribe/unsubscribe message format
{
  "op": "subscribe" | "unsubscribe",
  "channel": "level2" | "level1",
  "markets": string[]
}sample subscribe message
{
  "op": "subscribe",
  "channel": "level2",
  "markets": ["BTC-PERP"]
}Subscription confirmation message format
Once a subscription (or unsubscription) request is processed by the server, it will push subscribed (or unsubscribed) confirmation message or error if received request message was invalid.
{
"type": "subscribed" | "unsubscribed",
"channel": "level2" | "level1",
"markets": string[],
"timestamp": string
}sample subscribed confirmation message
{
  "type": "subscribed",
  "channel": "level2",
  "markets": ["BTC-PERP"],
  "timestamp": "2021-03-23T17:06:30.010Z"
}Error message format
Error message is pushed for invalid subscribe/unsubscribe messages - non existing market, invalid channel name etc.
{
  "type": "error",
  "message": "string,
  "timestamp": "string
}sample error message
{
  "type": "error",
  "message": "Invalid channel provided: 'levels1'.",
  "timestamp": "2021-03-23T17:13:31.010Z"
}Supported channels & corresponding message types
When subscribed to the channel, server will push the data messages as specified below.
- level1
- level2
Supported markets
Markets supported by mango-vial server can be queried via GET /markets HTTP endpoint ([].name field).
Data messages
- typeis determining message's data type so it can be handled appropriately
- timestampwhen message has been received from node RPC API in ISO 8601 format with milliseconds, for example: "2021-03-23T17:03:03.994Z"
- slotis a Solana's slot number for which message has produced
- versionof Serum DEX program layout (DEX version)
- priceand- sizeare provided as strings to preserve precision
quote
Pushed real-time for any change in best bid/ask price or size for a given market (decoded from the bids and asks accounts).
- bestAskand- bestBidare tuples where first item is a price and second is a size of the best bid/ask level
{
  "type": "quote",
  "market": string,
  "timestamp": string,
  "slot": number,
  "version": number,
  "bestAsk": [price: string, size: string] | undefined,
  "bestBid": [price: string, size: string] | undefined
}sample quote message
{
  "type": "quote",
  "market": "BTC-PERP",
  "timestamp": "2021-03-24T07:11:57.186Z",
  "slot": 70544253,
  "version": 3,
  "bestAsk": ["55336.1", "5.0960"],
  "bestBid": ["55285.6", "7.5000"]
}l2snapshot
Entire up-to-date order book snapshot with orders aggregated by price level pushed immediately after successful subscription confirmation.
- asksand- bidsarrays contain tuples where first item of a tuple is a price level and second one is a size of the resting orders at that price level
- it can be pushed for an active connection as well when underlying server connection to the RPC node has been restarted, in such scenario locally maintained order book should be re-initialized with a new snapshot 
- together with - l2updatemessages it can be used to maintain local up-to-date full order book state
{
  "type": "l2snapshot",
  "market": string,
  "timestamp": string,
  "slot": number,
  "version": number,
  "asks": [price: string, size: string][],
  "bids": [price: string, size: string][]
}sample l2snapshot message
{
  "type": "l2snapshot",
  "market": "BTC-PERP",
  "timestamp": "2021-03-24T09:00:53.087Z",
  "slot": 70555623,
  "version": 3,
  "asks": [
    ["56463.3", "8.6208"],
    ["56474.3", "5.8632"],
    ["56496.4", "3.7627"]
  ],
  "bids": [
    ["56386.0", "4.8541"],
    ["56370.1", "6.8054"],
    ["56286.3", "8.6631"]
  ]
}l2update
Pushed real-time for any change to the order book for a given market with updated price levels and sizes since the previous update (decoded from the bids and asks accounts).
- together with - l2snapshot,- l2updatemessages can be used to maintain local up-to-date full order book state
- asksand- bidsarrays contain updates which are provided as a tuples where first item is an updated price level and second one is an updated size of the resting orders at that price level (absolute value, not delta)
- if size is set to - 0it means that such price level does not exist anymore and shall be removed from locally maintained order book
{
  "type": "l2update",
  "market": string,
  "timestamp": string,
  "slot": number,
  "version": number,
  "asks": [price: string, size: string][],
  "bids": [price: string, size: string][]
}sample l2update message
{
  "type": "l2update",
  "market": "BTC-PERP",
  "timestamp": "2021-03-24T09:00:55.586Z",
  "slot": 70555627,
  "version": 3,
  "asks": [["56511.5", "7.5000"]],
  "bids": [
    ["56421.6", "0.0000"],
    ["56433.6", "5.9475"]
  ]
}HTTP API
GET /markets
Returns Mango Perp markets list supported by mango-vial instance (it can be updated by providing custom markets.json file).
Endpoint URL
- http://localhost:8000/v1/markets - assuming mango-vial runs locally on default port without SSL enabled 
- https://api.mango-vial.dev/v1/markets - demo mango-vial server endpoint 
Response format
{
  "name": string,
  "address": string,
  "programId": string,
}[]sample response
[
  {
    "name": "BTC-PERP",
    "address": "A8YFbxQYFVqKZaoYJLLUVcQiWP7G2MeEgW5wsAQgMvFw",
    "programId": "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin",
  }
]4 years ago