NAV
shell python javascript

Introduction

Leverj REST and Websocket API enable access to all features of the platform. The Kovan testnet, the Ropsten testnet, and the live sites are built entirely using this API and should be seen as one of the many possible platform implementations. The API could be leveraged to power a comprehensive set of tools and utilities, including automated trading clients and bots.

Language SDK/Libraries

Currently we support Python and Javascript (with node.js) for programmatic access.

Python

Javascript (Node.js)

Usage of these libraries will be illustrated in the following sections.

Spot & Futures Markets

Leverj provides access to two markets, the spot market and the derivatives market. The spot market supports ERC20 pairs. The derivatives market supports futures products. Perpetual swaps on BTC, ETH, and DEFI index are currently offered.

The API is very similar for both spot and futures markets, with minor differences to accomodate for product variations, market structure differences, and differing trading strategy possibilities between the two.

Scheme

The URL scheme like most REST apis consists of a BASE_URL and an endpoint.

REST URL = BASE_URL + ENDPOINT

Base URL

The BASE_URL is of the following format: <protocol>://<host>/<market>/api/v1

For REST calls https is the protocol, host depends on the environment, and market could be spot or futures.

The base url for spot REST API for the live site is https://live.leverj.io/spot/api/v1. For Kovan testnet use https://kovan.leverj.io/spot/api/v1.

For futures, the live site base url is https://live.leverj.io/futures/api/v1 and for Kovan it is https://kovan.leverj.io/futures/api/v1.

To request an endpoint, append it to the base url and make a request.

Getting All Info

For example to access the /all/info endpoint for the spot market use https://live.leverj.io/spot/api/v1/all/info. For futures use https://live.leverj.io/futures/api/v1/all/info.

curl https://live.leverj.io/spot/api/v1/all/info
import requests
response = requests.get('https://live.leverj.io/spot/api/v1/all/info')
response.status_code
response.json()
  const axios = require('axios');
  axios.get("https://live.leverj.io/spot/api/v1/all/info")
    .then(function (response) {
      console.log({ response });
  });

URL parameters

Url parameters are denoted by prepending a colon:

/instrument/:symbol/chart/:timeframe

The parameters :symbol and :timeframe need to be filled in when making a REST call to server. To get chart for a specific symbol with id L2ETH for timeframe 5, the actual url would be https://live.leverj.io/api/v1/instrument/L2ETH/chart/5

https://live.leverj.io/api/v1/instrument/LEVETH/chart/5

HTTP headers

HTTP 1.1 requires Host header. In the examples here, we have used testnet host kovan.leverj.io. For production use, you should change it to live.leverj.io. You need Authorization and Nonce headers for protected resources. You may also add other appropriate headers, which are omitted here for brevity.

Authentication

Leverj uses a zero-knowledge authentication system. Leverj replaces the typical username and password based authentication scheme with a triplet of your account id, an apikey and a secret associated with the apikey. There is absolutely no need for the system to know about your account's private key. The apikey's secret is used to sign and confirm your identity but is not transfered over to the server either. The loginless system relies on ECDSA (Elliptic Curve Digital Signature Algorithm). The scheme involves signing message payload using an apikey's secret and subsequently using the elliptic curve signature elements to derive or recover the apikey. The recovered apikey is matched against the registered apikey. This pair of actions involving signing and recovery establishes trust and identity to facilitate authentication. Every request is authenticated using this mechanism.

You need to register online with the exchange to setup and download an apikey and its corresponding secret. When you register, you will be given an option to download your api key. You can also download the key at a later time by accessing it from your Leverj Gluon wallet. The api key contains your accountId, apikey, and secret. It will download as a json formatted file. An account can have multiple pairs of apikeys and their corresponding secrets.

Zero knowledge Authentication avoids setting session cookies and eliminates the following classes of attacks: session hijacking, some kinds of replay attacks, cookie sniffing, and some XSS and CSRF attacks. Not having the password or session id on the server mitigates some kinds of attacks due to server breach. Zero knowledge systems never send passwords or cookies and are also safer in case of information leak from TLS issues such as the Heartbleed bug.

Overview

Leverj authentication requires you to set Authorization and Nonce headers in HTTP for protected endpoints. This requires your account id, apikey and secret, which are in a JSON key file that you saved when you registered with the exchange and setup your apikey.

Authorization and Nonce headers

The syntax for Authorization and Nonce headers is as below.

Authorization: SIGN <account_id>.<apiKey>.<v>.<r>.<s>
Nonce: <unix_time>

v, r, and s are ECDSA related elements. r and s are normal outputs of an ECDSA signature and v is the extra byte or header byte that helps in public key recovery.

For example, to get all configuration information:

GET /api/v1/all/config HTTP/1.1
Host: test.leverj.io
Authorization: 'SIGN 0xE239Caeb4A6eCe2567fa5307f6b5D95149a5188F.0x12d80a4b0803Cf7D462EDF36963429B6aCfA3fFa.27.0x50eef94b262808ea42072dbe4375d0e908efea6e713b37b8ed729a63e56e4c23.0x4cf4c460baf43092b5350692179a833feb9dd855d6421570a2dc7d1f3325a952'
Nonce: 1550791374642

/all/config is an open endpoint and you don't need an API key to access it but similar header and signature structures are used when getting protected information.

We provide transparent and seamless authentication support for node.js via an easy to use javascript module and suggest you use it instead of rolling out your own.

zka node module

The zka node module will do all the handshake and authentication and enables interaction with REST and socket API transparently. This may be used from a browser using browserify.

Remember to initialize and configure your zka instance before making calls to the API endpoints.

const zka = require("zka")(baseUrl, "/api/v1")
zka.init(accountId, apiKey, secret)

Authentication with zka

Nonce

To prevent replay attacks, all requests should include a nonce. The server expects UNIX time as the nonce. This requires a reasonably accurate clock on your client machine.

Nonce: 1550791374642

Clients with inaccurate clocks

If your client does not have an accurate clock or you are on an unusually slow network connection, you can compute the skew and apply it to all future requests using the Server-Time header in the HTTP responses. The zka node module does skew adjustment automatically. The non-cacheable HTTP methods PUT, POST, DELETE and OPTIONS return a Server-Time header.

Futures REST API

Unprotected REST API Endpoints

Unprotected endpoints do not require an Authorization header.

General Exchange Data

Method Rest Endpoint Description
GET /all/info Last price, 24Hr volume, etc

Exchange Configuration

Method Rest Endpoint Description
GET /all/config Get exchange configuration parameters

Market Data

Method Rest Endpoint Description
GET /instrument/:symbol/trade Get recent trade data
GET /instrument/:symbol/chart/:timeframe Get chart data for instrument
GET /instrument/:symbol/orderbook Get order book for the instrument

Futures WebSocket API

Leverj provides high speed and high throughput websocket based connectivity for all its endpoints. Use socket.io libraries to connect via websocket.

Here are a few language binding to consider:

When using websocket remember that the conversation occurs by means of emitting events and messages and listening to topics for data. When a client needs to communicate to the server it will emit an event with some data pertaining to the event. To receive data from the server the client listens on topics it is interested in and attaches callback functions to these listeners. When new data is published on such a topic, the client receives it and the client callback function is invoked. Client programs on receiving data could then proceed to use that data.

Connect & Register

As a first step, make sure to connect. You will need to connect to receive all and any data from websocket, including data from open endpoints. For protected endpoints, register in addition to connecting. You could think of registration as a login or authorization step.

Disconnect and unregister are actions that help cleanup and close the websocket connection.

Format & Helper Methods for Protected Endpoints

We will discuss details about each of the open and protected endpoints in the next few sections. In this section we touch upon an important pre-requisite for fetching protected data, i.e. the data that needs authentication.

Start by registering and downloading an APIKey. You will need an APIKey for each environment. You will have separate keys for testnet and the live environments. It's not the same. The APIKey is a JSON formatted text file that contains information on the following:

Collectively, this triplet are the credentials. Sometimes, it's also referred to in this documentation and in the example code as originator credentials. These credentials are available only to the user or caller of the API. Leverj has no access to the apiKey secret and cannot help in recovering it if you lose it. However, you are able to generate a new APIKey if you lose one.

When emitting a message to a server to fetch data from a protected endpoint you need to send event and data. event is a concatenation of the corrsponding method and uri. As an example to create an order you use the event POST /order. The data part of the message that is emitted needs adherering to certain requirements in terms of format and signatures. There is a helper method that provides a utility method to generate event and data by passing in the request payload and the credentials.

Look at the protected_endpoint_request function in leverj.websocket.util.py for an example implementation. You are welcome to use this method as is in your code or rewrite it in a a way that suits you.

Connect & Disconnect

Method Action Description
connect Connect to websocket
disconnect Disconnect

Register & Un-Register

Method Action Description
GET register Register/authorize
GET unregister Un-register/logout

Open Websocket Topics

Unprotected endpoints do not require Authorization.

Market Data

Method Action Description
orderbook Order book data, updated every minute
difforderbook Get change in order book
index Fetch indices

Protected Websocket Endpoints

All user specific endpoints require Authorization and Nonce information. These are included in the payload as headers.

Order

Method Endpoint/Topic Description
POST /order Create orders
DELETE /order Delete specified orders
order_execution Order fills

Listen to these topics to be notified as you add, update, and delete orders. Also, listen to order_error.

Topic Description
order_add Create order
order_update Update order
order_del Cancel order
order_error Order error

Position

Method Endpoint/Topic Description
position Position updates
liquidation Liquidation updates
adl ADL updates

Account

Method Endpoint/Topic Description
account_balance Receive data on account balance

Spot REST API

Unprotected REST API Endpoints

Unprotected endpoints do not require an Authorization header.

General Exchange Data

Method Rest Endpoint Description
GET /all/info Last price, 24Hr volume, etc

Exchange Configuration

Method Rest Endpoint Description
GET /all/config Get exchange configuration parameters

Market Data

Method Rest Endpoint Description
GET /instrument/:symbol/trade Get recent trade data
GET /instrument/:symbol/chart/:timeframe Get chart data for instrument
GET /instrument/:symbol/orderbook Get order book for the instrument

Protected REST API endpoints

All user specific endpoints require Authorization and Nonce headers as described in the Loginless or ZKA section

Open Orders

Method Rest Endpoint Description
GET /order Get all open orders
POST /order Create orders
PUT /order Update Orders
DELETE /order/:uuids Delete specified orders

Get account information: Orders and Trades

Method Rest Endpoint Description
GET /account
GET /account/execution GET User's recent executions

Examples (Futures)

Examples are structured as follows:

Section Description
Endpoint The /api endpoint
Client Request Request using the zka client
Server Response JSON Response payload (abbreviated to partial data sets for readability)

Exchange Basic Info

GET /all/info

curl https://live.leverj.io/futures/api/v1/all/info
response = requests.get('https://live.leverj.io/futures/api/v1/all/info')
response.json()
zka.rest.get('/all/info').then(function(result) {console.log(result)})

Response

Returns information on

for each of the perpetual swap instruments.

BTCUSD perpetual swap has id 1 and ETHUSD perpetual swap has id 2.

{
    "1": {
        "vol24H": {
            "qty": 0,
            "eth": 0,
            "priceChange": 0,
            "priceChangePercent": 0,
            "instrument": "1"
        },
        "lastPrice": 15072,
        "bid": 15532,
        "ask": 15688,
        "fundingRate": {
            "rate": "2098600000000000000",
            "start": 1605142800000,
            "end": 1605146400000,
            "instrument": "1",
            "indexPrice": 15596,
            "futurePrice": 15619
        },
        "index": {
            "date": 1605143249,
            "price": 15585,
            "topic": "index_BTCUSD"
        }
    },
    "2": {
        "vol24H": {
            "qty": 1.3,
            "eth": 596.4,
            "priceChange": -2.7,
            "priceChangePercent": -0.5869565217391,
            "instrument": "2"
        },
        "lastPrice": 457.3,
        "bid": 455.1,
        "ask": 459.7,
        "fundingRate": {
            "rate": "22000000000000000",
            "start": 1605142800000,
            "end": 1605146400000,
            "instrument": "2",
            "indexPrice": 457.8,
            "futurePrice": 458.4
        },
        "index": {
            "date": 1605143249,
            "price": 457.52,
            "topic": "index_ETHUSD"
        }
    }
}

Exchange config (format subject to frequent change)

GET /all/config

curl https://live.leverj.io/futures/api/v1/all/config
response = requests.get('https://live.leverj.io/futures/api/v1/all/config')
response.json()
zka.rest.get('/all/config').then(function(result) {console.log(result)})

Response

Returns configuration information about

Response

{
    "config": {
        "maxInputs": 50,
        "maxOrdersCreateUpdate": 20,
        "fee": {
            "default": {
                "maker": 0,
                "taker": 5
            },
            "factor": 10000
        },
        "estimatedTokenTransferFee": "100000",
        "network": {
            "provider": "mainnet",
            "etherscan": "https://etherscan.io",
            "id": 1,
            "appId": 3,
            "gluon": "0x75ACe7a086eA0FB1a79e43Cc6331Ad053d8C67cB",
            "legacyTokensExtension": "0xDA88EfA53c85Afa30564bb651A2E76b99a232082",
            "lev": "0x0F4CA92660Efad97a9a70CB0fe969c755439772C",
            "registryLogic": "0x385827aC8d1AC7B2960D4aBc303c843D9f87Bb0C",
            "registryData": "0x0fC25C7931679B838209c484d49Df0Cb9E633C41",
            "stakeLogic": "0xe517af2457E0dD285ed22Ee4440b265f203D1B0d",
            "stakeLogicV1": "0x88Ac1E78b8a7D2B457Cb030978F71EdeE541bD5b",
            "stakeData": "0xaB3AC436D66CBEeDc734ed2c1562c3a213c9bc77",
            "derivativesLogic": "0xDfBFe895e07e5115773Cb9631CB2148114589caC",
            "derivativesData": "0x563052914Fd973a2305763269A106a7B0B6D50Cc"
        },
        "minimumDepositConfirmations": 25,
        "geofence": {
            "block": ["US", "PR", "AS", "VI", "GU", "UM", "SC", "CU", "SY", "SD", "IR", "KP", "UA:40", "UA:43"]
        },
        "noSignup": false,
        "maintenance": false,
        "tradingDisabled": false,
        "depositGasLimit": 160000,
        "gasPriceMultiplier": 1.5,
        "default": {
            "instrument": "1"
        },
        "markets": {
            "DAI": true
        },
        "legacyTokensExtensionSupport": {
            "USDT": true
        },
        "defaultDisplayPair": "DAIUSDC"
    },
    "instruments": {
        "1": {
            "id": "1",
            "symbol": "BTCUSD",
            "name": "BTC/USD",
            "status": "active",
            "tickSize": 1,
            "quoteSignificantDigits": 0,
            "baseSignificantDigits": 4,
            "baseSymbol": "BTC",
            "quoteSymbol": "DAI",
            "maxLeverage": 100,
            "topic": "index_BTCUSD",
            "quote": {
                "name": "DAI",
                "address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
                "symbol": "DAI",
                "decimals": 18
            }
        },
        "2": {
            "id": "2",
            "symbol": "ETHUSD",
            "name": "ETH/USD",
            "status": "active",
            "tickSize": 0.1,
            "quoteSignificantDigits": 1,
            "baseSignificantDigits": 2,
            "baseSymbol": "ETH",
            "quoteSymbol": "DAI",
            "maxLeverage": 50,
            "topic": "index_ETHUSD",
            "quote": {
                "name": "DAI",
                "address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
                "symbol": "DAI",
                "decimals": 18
            }
        }
    },
    "assets": {
        "DAI": {
            "name": "DAI",
            "address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
            "symbol": "DAI",
            "decimals": 18
        },
        "L2": {
            "name": "L2",
            "address": "0xBbff34E47E559ef680067a6B1c980639EEb64D24",
            "symbol": "L2",
            "decimals": 18
        }
    },
    "user": {
        "ip": "<IP Address>",
        "country": ["<country>", "<province>"]
    }
}

Recent trade data for an instrument (e.g. LEVETH)

GET /instrument/:symbol/trade

zka.rest.get('/instrument/LEVETH/trade').then(function(result) {console.log(result)})

Response

[ { date: 1550710792,
    price: 0.000102,
    volume: 0.2,
    instrument: 'LEVETH',
    side: 'buy' },
  { date: 1550708634,
    price: 0.000101,
    volume: 1,
    instrument: 'LEVETH',
    side: 'buy' },
  { date: 1550687614,
    price: 0.0001,
    volume: 1,
    instrument: 'LEVETH',
    side: 'buy' },
  { date: 1550682148,
    price: 0.0001,
    volume: 92,
    instrument: 'LEVETH',
    side: 'sell' },
  { date: 1550682147,
    price: 0.0001,
    volume: 6,
    instrument: 'LEVETH',
    side: 'buy' },
  { date: 1550682142,
    price: 0.0001,
    volume: 94,
    instrument: 'LEVETH',
    side: 'sell' } 
]

Chart for instrument

GET /instrument/:symbol/chart/:timeframe

zka.rest.get('/instrument/LEVETH/chart/5').then(function(result) {console.log(result)})

Response

[ { v: 0,
    s: 0,
    t: 1550794200,
    o: 0.000102,
    h: 0.000102,
    l: 0.000102,
    c: 0.000102,
    i: 'LEVETH' },
  { v: 0,
    s: 0,
    t: 1550793900,
    o: 0.000102,
    h: 0.000102,
    l: 0.000102,
    c: 0.000102,
    i: 'LEVETH' },
  { v: 0,
    s: 0,
    t: 1550793600,
    o: 0.000102,
    h: 0.000102,
    l: 0.000102,
    c: 0.000102,
    i: 'LEVETH' },
  { v: 0,
    s: 0,
    t: 1550793300,
    o: 0.000102,
    h: 0.000102,
    l: 0.000102,
    c: 0.000102,
    i: 'LEVETH' },
  { v: 0,
    s: 0,
    t: 1550793000,
    o: 0.000102,
    h: 0.000102,
    l: 0.000102,
    c: 0.000102,
    i: 'LEVETH' },
  { v: 0,
    s: 0,
    t: 1550792700,
    o: 0.000102,
    h: 0.000102,
    l: 0.000102,
    c: 0.000102,
    i: 'LEVETH' }
]

Order book for the instrument

GET /instrument/:symbol/orderbook

zka.rest.get('/instrument/LEVETH/orderbook').then(function(result) {console.log(result)})

Response

{ bid: 0.000093,
  ask: 0.000102,
  buy:
   [ { price: 0.000093,
       numberOfOrders: 1,
       totalQuantity: 100,
       instrument: 'LEVETH' },
     { price: 0.000092,
       numberOfOrders: 1,
       totalQuantity: 100,
       instrument: 'LEVETH' }],
  sell:
   [ { price: 0.000102,
       numberOfOrders: 1,
       totalQuantity: 0.2,
       instrument: 'LEVETH' },
     { price: 0.000103,
       numberOfOrders: 1,
       totalQuantity: 100,
       instrument: 'LEVETH' }] 
}

Get all open orders

GET /order

zka.rest.get('/order').then(function(result) {console.log(result)})

Response

[ { uuid: '77bb9a50-3639-11e9-aa67-1b934017d41a',
    accountId: '0xE239Caeb4A6eCe2567fa5307f6b5D95149a5188F',
    side: 'buy',
    quantity: 9,
    filled: 0,
    averagePrice: 0,
    cancelled: 0,
    price: 0.001209,
    entryTime: 1550795606133888,
    eventTime: 1550795606133888,
    status: 'open',
    orderType: 'LMT',
    instrument: 'LEVETH',
    timestamp: 1550795605937000,
    signature:
     '0x2de825f6315f3a712f0b69c36b690918466889edcebbd86e086c4b5e8715c9fa2bd4e74bf0cb263d2c5464b7eba5777f94246989d4f98707c9f44aa891b4743900',
    token: '0xAa7127e250E87476FdD253f15e86A4Ea9c4c4BD4',
    clientOrderId: 'ac60c4f4-bd6b-40a4-ae1d-bbef604d0bb7',
    originator: '0x12d80a4b0803Cf7D462EDF36963429B6aCfA3fFa' },
  { uuid: 'b7bf15a0-3639-11e9-b2cc-fe32bd3894ee',
    accountId: '0xE239Caeb4A6eCe2567fa5307f6b5D95149a5188F',
    side: 'buy',
    quantity: 8,
    filled: 0,
    averagePrice: 0,
    cancelled: 0,
    price: 0.001213,
    entryTime: 1550795713530585,
    eventTime: 1550795713530585,
    status: 'open',
    orderType: 'LMT',
    instrument: 'LEVETH',
    timestamp: 1550795713339000,
    signature:
     '0x962f348852da325d8663d25ee922f67b278929d500b68234ee0e294d0b4f7e601a957c8798a7fb6d26fb2bc477d3c30293790d9a64e68a3e87d6ae433432b33d01',
    token: '0xAa7127e250E87476FdD253f15e86A4Ea9c4c4BD4',
    clientOrderId: 'a0da5818-b0a3-4e39-b8eb-f5f7f7384bfe',
    originator: '0x12d80a4b0803Cf7D462EDF36963429B6aCfA3fFa' } 
]

Fetch all futures order books

Listen on the topic: 'orderbook'

Connect to the socket endpoint and listen on "orderbook". A simple code illustration in python and a sample response is provided.

The entire orderbook is returned when listening on this topic. Filter down to the specific instrument as required. Remember that this feed updates every minute and gives you the entire order book. If you want to fetch this and also listen to order books as soon as there is any chance then listen to difforderbook in addition to this topic.

The instruments currently supported are:

sio = socketio.Client(logger=False, engineio_logger=False)
sio.connect('https://kovan.leverj.io', socketio_path='/futures/socket.io')
sio.on("orderbook", on_orderbook)

def on_orderbook(data):
        print(f'orderbook data: {data}')

Response

{
    '1': {
        'buy': [],
        'sell': []
    },
    '2': {
        'buy': [],
        'sell': []
    },
    '3': {
        'bid': 56904,
        'ask': 56914,
        'buy': [{
            'price': 56904,
            'numberOfOrders': 1,
            'totalQuantity': 0.01,
            'instrument': '3'
        }, {
            'price': 56899,
            'numberOfOrders': 1,
            'totalQuantity': 0.01,
            'instrument': '3'
        }, {
            'price': 56894,
            'numberOfOrders': 1,
            'totalQuantity': 0.01,
            'instrument': '3'
        }, {
            'price': 56889,
            'numberOfOrders': 1,
            'totalQuantity': 0.01,
            'instrument': '3'
        }, {
            'price': 56884,
            'numberOfOrders': 1,
            'totalQuantity': 0.01,
            'instrument': '3'
        }],
        'sell': [{
            'price': 56914,
            'numberOfOrders': 1,
            'totalQuantity': 0.01,
            'instrument': '3'
        }, {
            'price': 56919,
            'numberOfOrders': 1,
            'totalQuantity': 0.01,
            'instrument': '3'
        }, {
            'price': 56924,
            'numberOfOrders': 1,
            'totalQuantity': 0.01,
            'instrument': '3'
        }, {
            'price': 56929,
            'numberOfOrders': 1,
            'totalQuantity': 0.01,
            'instrument': '3'
        }, {
            'price': 56934,
            'numberOfOrders': 1,
            'totalQuantity': 0.01,
            'instrument': '3'
        }]
    },
    '4': {
        'bid': 1718.5,
        'ask': 1720.5,
        'buy': [{
            'price': 1718.5,
            'numberOfOrders': 1,
            'totalQuantity': 1.65,
            'instrument': '4'
        }, {
            'price': 1717.9,
            'numberOfOrders': 1,
            'totalQuantity': 0.57,
            'instrument': '4'
        }, {
            'price': 1717.4,
            'numberOfOrders': 1,
            'totalQuantity': 0.22,
            'instrument': '4'
        }, {
            'price': 1716.9,
            'numberOfOrders': 1,
            'totalQuantity': 0.04,
            'instrument': '4'
        }],
        'sell': [{
            'price': 1720.5,
            'numberOfOrders': 1,
            'totalQuantity': 1.65,
            'instrument': '4'
        }, {
            'price': 1721,
            'numberOfOrders': 1,
            'totalQuantity': 0.57,
            'instrument': '4'
        }, {
            'price': 1721.6,
            'numberOfOrders': 1,
            'totalQuantity': 0.22,
            'instrument': '4'
        }, {
            'price': 1722.1,
            'numberOfOrders': 1,
            'totalQuantity': 0.04,
            'instrument': '4'
        }]
    }
}

Fetch all updates to order book

Listen on the topic: 'difforderbook'

Connect to the socket endpoint and listen on "difforderbook". A simple code illustration in python and a sample response is provided.

Updates to orderbook are emitted as soon as it occurs. You could use the orderbook feed to get snapshots of order books every minute and then use difforderbook to listen to every update in the book between updates from the order book feed. Remember, you will only receive the delta. Filter down to the specific instrument as required.

The instruments currently supported are:

sio = socketio.Client(logger=False, engineio_logger=False)
sio.connect('https://kovan.leverj.io', socketio_path='/futures/socket.io')
sio.on("difforderbook", on_difforderbook)

def on_difforderbook(data):
        print(f'difforderbook data: {data}')

Response

On testnet only the DEFIUSD market maker was quoting so you get difforderbook updates as it adjusts the quotes.

{
    'instrument': '5',
    'buy': {
        '490': {
            'price': 490,
            'instrument': {
                'id': '5',
                'symbol': 'DEFIUSDT',
                'name': 'DEFI/USD (USDT)',
                'status': 'active',
                'tickSize': 0.1,
                'quoteSignificantDigits': 1,
                'baseSignificantDigits': 2,
                'baseSymbol': 'DEFI',
                'quoteSymbol': 'USDT',
                'maxLeverage': 50,
                'topic': 'index_DEFI',
                'quote': {
                    'name': 'USDT',
                    'address': '0x6a4480B1c08A822Fed4b907AD09798ED79312a44',
                    'symbol': 'USDT',
                    'decimals': 6
                }
            },
            'numberOfOrders': 0,
            'totalQuantity': 0
        },
        '490.5': {
            'price': 490.5,
            'instrument': {
                'id': '5',
                'symbol': 'DEFIUSDT',
                'name': 'DEFI/USD (USDT)',
                'status': 'active',
                'tickSize': 0.1,
                'quoteSignificantDigits': 1,
                'baseSignificantDigits': 2,
                'baseSymbol': 'DEFI',
                'quoteSymbol': 'USDT',
                'maxLeverage': 50,
                'topic': 'index_DEFI',
                'quote': {
                    'name': 'USDT',
                    'address': '0x6a4480B1c08A822Fed4b907AD09798ED79312a44',
                    'symbol': 'USDT',
                    'decimals': 6
                }
            },
            'numberOfOrders': 0,
            'totalQuantity': 0
        },
        '489.4': {
            'price': 489.4,
            'instrument': {
                'id': '5',
                'symbol': 'DEFIUSDT',
                'name': 'DEFI/USD (USDT)',
                'status': 'active',
                'tickSize': 0.1,
                'quoteSignificantDigits': 1,
                'baseSignificantDigits': 2,
                'baseSymbol': 'DEFI',
                'quoteSymbol': 'USDT',
                'maxLeverage': 50,
                'topic': 'index_DEFI',
                'quote': {
                    'name': 'USDT',
                    'address': '0x6a4480B1c08A822Fed4b907AD09798ED79312a44',
                    'symbol': 'USDT',
                    'decimals': 6
                }
            },
            'numberOfOrders': 1,
            'totalQuantity': 0.17
        },
        '489.5': {
            'price': 489.5,
            'instrument': {
                'id': '5',
                'symbol': 'DEFIUSDT',
                'name': 'DEFI/USD (USDT)',
                'status': 'active',
                'tickSize': 0.1,
                'quoteSignificantDigits': 1,
                'baseSignificantDigits': 2,
                'baseSymbol': 'DEFI',
                'quoteSymbol': 'USDT',
                'maxLeverage': 50,
                'topic': 'index_DEFI',
                'quote': {
                    'name': 'USDT',
                    'address': '0x6a4480B1c08A822Fed4b907AD09798ED79312a44',
                    'symbol': 'USDT',
                    'decimals': 6
                }
            },
            'numberOfOrders': 0,
            'totalQuantity': 0
        },
        '489.9': {
            'price': 489.9,
            'instrument': {
                'id': '5',
                'symbol': 'DEFIUSDT',
                'name': 'DEFI/USD (USDT)',
                'status': 'active',
                'tickSize': 0.1,
                'quoteSignificantDigits': 1,
                'baseSignificantDigits': 2,
                'baseSymbol': 'DEFI',
                'quoteSymbol': 'USDT',
                'maxLeverage': 50,
                'topic': 'index_DEFI',
                'quote': {
                    'name': 'USDT',
                    'address': '0x6a4480B1c08A822Fed4b907AD09798ED79312a44',
                    'symbol': 'USDT',
                    'decimals': 6
                }
            },
            'numberOfOrders': 1,
            'totalQuantity': 0.5
        },
        '490.4': {
            'price': 490.4,
            'instrument': {
                'id': '5',
                'symbol': 'DEFIUSDT',
                'name': 'DEFI/USD (USDT)',
                'status': 'active',
                'tickSize': 0.1,
                'quoteSignificantDigits': 1,
                'baseSignificantDigits': 2,
                'baseSymbol': 'DEFI',
                'quoteSymbol': 'USDT',
                'maxLeverage': 50,
                'topic': 'index_DEFI',
                'quote': {
                    'name': 'USDT',
                    'address': '0x6a4480B1c08A822Fed4b907AD09798ED79312a44',
                    'symbol': 'USDT',
                    'decimals': 6
                }
            },
            'numberOfOrders': 1,
            'totalQuantity': 1.5
        }
    },
    'sell': {
        '493': {
            'price': 493,
            'instrument': {
                'id': '5',
                'symbol': 'DEFIUSDT',
                'name': 'DEFI/USD (USDT)',
                'status': 'active',
                'tickSize': 0.1,
                'quoteSignificantDigits': 1,
                'baseSignificantDigits': 2,
                'baseSymbol': 'DEFI',
                'quoteSymbol': 'USDT',
                'maxLeverage': 50,
                'topic': 'index_DEFI',
                'quote': {
                    'name': 'USDT',
                    'address': '0x6a4480B1c08A822Fed4b907AD09798ED79312a44',
                    'symbol': 'USDT',
                    'decimals': 6
                }
            },
            'numberOfOrders': 0,
            'totalQuantity': 0
        },
        '492.4': {
            'price': 492.4,
            'instrument': {
                'id': '5',
                'symbol': 'DEFIUSDT',
                'name': 'DEFI/USD (USDT)',
                'status': 'active',
                'tickSize': 0.1,
                'quoteSignificantDigits': 1,
                'baseSignificantDigits': 2,
                'baseSymbol': 'DEFI',
                'quoteSymbol': 'USDT',
                'maxLeverage': 50,
                'topic': 'index_DEFI',
                'quote': {
                    'name': 'USDT',
                    'address': '0x6a4480B1c08A822Fed4b907AD09798ED79312a44',
                    'symbol': 'USDT',
                    'decimals': 6
                }
            },
            'numberOfOrders': 1,
            'totalQuantity': 1.5
        }
    },
    'bid': 490.4,
    'ask': 492.4
}

Fetch indices

Listen on the topic: 'index'

Connect to the socket endpoint and listen on "index". A simple code illustration in python and a sample response is provided.

The value for all indices is returned in real time, as available. Filter down to the specific index as required.

The indices currently supported are:

sio = socketio.Client(logger=False, engineio_logger=False)
sio.connect('https://kovan.leverj.io', socketio_path='/futures/socket.io')
sio.on("index", on_index)

def on_index(data):
        print(f'index data: {data}')

Response

on_index data: {'date': 1618952192, 'topic': 'index_BTCUSD'}
on_index data: {'date': 1618952192, 'topic': 'index_DEFI'}
on_index data: {'date': 1618952192, 'topic': 'index_ETHUSD'}
on_index data: {'date': 1618952192, 'topic': 'index_LINK'}
on_index data: {'date': 1618952193, 'price': 2309.6, 'topic': 'index_ETHUSD', 'stale': False}
on_index data: {'date': 1618952193, 'price': 56854.1, 'topic': 'index_BTCUSD', 'stale': False}
on_index data: {'date': 1618952194, 'price': 56852.6, 'topic': 'index_BTCUSD', 'stale': False}
on_index data: {'date': 1618952195, 'price': 2309.2, 'topic': 'index_ETHUSD', 'stale': False}
on_index data: {'date': 1618952195, 'price': 56847.6, 'topic': 'index_BTCUSD', 'stale': False}
on_index data: {'date': 1618952196, 'price': 451.1, 'topic': 'index_DEFI', 'stale': False}

Connect via websocket

Connect to Leverj socket.io endpoint

Connect using one of the socket.io language bindings. An example in Python is included.

As a first step instantiate a socket.io client. Register an event listener for the "connect" event. This would allow you to be alerted on a successful connection to the websocket endpoint.

Use the Leverj host and path to connect to the websocket endpoint.

For kovan testnet the host value is https://kovan.leverj.io and path is /futures/socket.io. For livenet the path is the same but the host changes to https://live.leverj.io.

sio = socketio.Client(logger=False, engineio_logger=False)
sio.on("connect", on_connect)
sio.connect('https://kovan.leverj.io', socketio_path='/futures/socket.io')

def on_connect(data):
    print('connected!)

Response

If connected successfully, you should see the message "connected!".

**** response ****
connected!

Disconnect from a websocket

Disconnect from a connected Leverj socket.io endpoint

Disconnect using one of the socket.io language bindings. An example in Python is included.

Register an event listener for the "disconnect" event. This would allow you to be alerted when you successfully disconnect from an existing websocket connection.

sio = socketio.Client(logger=False, engineio_logger=False)
sio.on("disconnect", on_disconnect)
sio.disconnect()

def on_disconnect(data):
    print('disconnected!)

Response

If the socket connection is disconnected successfully, you should see the message "disconnected!".

**** response ****
disconnected!

Register

Register on a connected Leverj socket.io endpoint

Registration allows you to authenticate and listen to your own personal events from protected endpoints.

As a first step instantiate a socket.io client. Connect and then register.

Please look at the websocket client example and the utility functions in leverj.websocket.util.py.

def register(originator_credentials):
    request_body = {"accountId": originator_credentials.get(
        'accountId'), "apiKey": originator_credentials.get('apiKey')}
    request = {
        "method": "GET",
        "uri": "/register",
        "headers": {},
        "body": json.dumps(request_body),
        "params": {}
    }
    return request

request = register(originator_credentials)

# protected_endpoint_request util method adds required signature headers and formats the request
protected_request_payload = protected_endpoint_request(request, originator_credentials)

Response

You will not receive an explicit response on successful registration. If the registration fails you will not receive data from protected endpoints.

Unregister

Stop listening for events from Leverj socket.io protected endpoint

Unregister is analogous to logout.

Once you successfully unregister, you will stop listening to data from events for protected endpoints that need authorization.

def unregister(originator_credentials):
    request_body = {"accountId": originator_credentials.get(
        'accountId'), "apiKey": originator_credentials.get('apiKey')}
    request = {
        "method": "GET",
        "uri": "/unregister",
        "headers": {},
        "body": json.dumps(request_body),
        "params": {}
    }
    return request

request = unregister(originator_credentials)

# protected_endpoint_request util method adds required signature headers and formats the request
protected_request_payload = protected_endpoint_request(request, originator_credentials)

Response

You will not receive an explicit response on unregistration.

Create Order

Create an order and send it via websocket

Before you create an order, take a moment to look at the product specification at https://leverj.io/. Leverj Futures offers perpetual swap contracts. These are linear contracts, where margin and settelment is in stablecoins. Both DAI and USDT margined and settled contracts are available for BTCUSD and ETHUSD. The DEFIUSD contract is offered only with USDT as the margin and settlement currency.

Leverj supports a number of advanced order types. Please read Leverj Futures Order Types for details.

The following attributes are supported for an order:

Attribute/Property Description
accountId Account address of the trader. Credentials or APIKey has this information
originator APIKey. Credentials or APIKey has this information
instrument The instrument id
price Price at which you want to buy or sell. Adjust price precision basis supported quoteSignificantDigits for the instrument
quantity Amount you want to buy or sell. Adjust quantity precision basis supported baseSignificantDigits for the instrument
marginPerFraction Perpetual swaps are leveraged contracts so you could put down substantially less margin than the notional value you buy or sell. This is the margin in lowest denomination of base significant digits
side Order side. Choices are "buy" or "sell"
orderType Multiple order types are supported. LMT, MKT, SMKT, SLMT are supported
timestamp Timestamp when order is created. Make sure to have this in milliseconds
quote Address of the quote currency
isPostOnly Boolean value to flag is it's a post only order
reduceOnly Boolean value to flag is it's a reduce only order
clientOrderId User generated order id that could be used to query the status of an order
triggerPrice Trigger price when an order is activated. For example, a stop-limit orders gets activated only when the stop order trigger price is reached
indexSanity A sanity check to define what variance from the index should be allowed

Once you create an order. You need to sign it. A signed order becomes the body of a request that needs to be transformed for a protected websocket request.

Once you have a protected request ready, you emit it from the websocket client to the Leverj endpoint for creating orders. Multiple orders can be created in a single call.

Example code for Python is included.

Start by looking at the order creation and signing functions. Then look at the method for created a protected request order creation payload. A working example is available in websocket_client_example.py.

def get_margin_per_fraction(self, orderInstrument, price, leverage):
    estimated_entry_price = price
    max_leverage = orderInstrument['maxLeverage']
    if leverage > max_leverage:
        self.logger.info(
            f'You have specified a leverage of {leverage} but the max leverage allowed on this instrument is {max_leverage}.')
    base_significant_digits = orderInstrument['baseSignificantDigits']
    decimals = orderInstrument['quote']['decimals']
    multiplier = Decimal(
        pow(Decimal(10), Decimal(decimals - base_significant_digits)))
    intermediate_value = Decimal((Decimal(
        estimated_entry_price) * multiplier) / Decimal(leverage)).to_integral_exact()
    return int(Decimal(intermediate_value) * Decimal(pow(Decimal(10), Decimal(base_significant_digits))))

def create_futures_order(self, side, price, triggerPrice, quantity, orderInstrument, orderType, leverage, orderAccountId, originatorApiKey, secret, reduceOnly=False):
    price_precision = orderInstrument.get('quoteSignificantDigits')
    quantity_precision = orderInstrument.get('baseSignificantDigits')
    # default leverage is set to 1.0 which means you aren't using any leverage. If you want 5K DAI position to control 10K DAI worth of BTC, use leverage of 2
    order = {
        'accountId': orderAccountId,
        'originator': originatorApiKey,
        'instrument': orderInstrument['id'],
        'price': round_with_precision(price, price_precision),
        'quantity': round_with_precision(quantity, quantity_precision),
        'marginPerFraction': str(self.get_margin_per_fraction(orderInstrument, price, leverage)),
        'side': side,
        'orderType': orderType,
        'timestamp': int(time.time()*1000000),
        'quote': orderInstrument['quote']['address'],
        'isPostOnly': False,
        'reduceOnly': reduceOnly,
        'clientOrderId': 1,
        'triggerPrice': round_with_precision(triggerPrice, price_precision),
        'indexSanity': MAX_INDEX_VARIANCE
    }
    order['signature'] = futures.sign_order(
        order, orderInstrument, secret)
    return order

def create_order(originator_credentials):
    client = Client('./resources/config/kovan.leverj.io/c21b18-64bdd3.json')
    client.set_api_url('https://kovan.leverj.io/futures/api/v1')
    all_config = client.get_all_config()
    logging.debug(f'all info: {all_config}')
    instruments = all_config.get('instruments')
    BTCDAI_instrument = instruments.get('1')
    print(f'BTCDAI_instrument: {BTCDAI_instrument}')
    futures_order = client.create_futures_order('buy', 55394, 55394, 0.02, BTCDAI_instrument, 'LMT', 2.0, originator_credentials.get(
        'accountId'), originator_credentials.get('apiKey'), originator_credentials.get('secret'))
    print(f'futures_order: {futures_order}')
    body = json.dumps([futures_order], separators=(',', ':'))
    request = {
        "method": "POST",
        "uri": "/order",
        "headers": {"requestid": str(uuid.uuid4())},
        "body": body,
        "params": {"instrument": BTCDAI_instrument.get('id')}
    }
    return request

request = create_order(originator_credentials)

# protected_endpoint_request util method adds required signature headers and formats the request
protected_request_payload = protected_endpoint_request(request, originator_credentials)


Response

On successful order creation you should receive the newly created order as data on the order_add topic.

**** response ****
on_order_add: {'result': [{'uuid': '9c8e9a00-a304-11eb-be7d-fc18fda03052', 'accountId': '0xc21b183A8050D1988117B86408655ff974d021A0', 'side': 'buy', 'quantity': 0.02, 'filled': 0, 'averagePrice': 0, 'cancelled': 0, 'price': 53863, 'triggerPrice': 53863, 'marginPerFraction': '26931500000000000000000', 'entryTime': 1619052576672616, 'eventTime': 1619052576672616, 'status': 'open', 'orderType': 'LMT', 'instrument': '1', 'timestamp': 1619052575006805, 'signature': '0x015f2a9a3841cefc6d49e41d82f0f31fee1d6bea8bc77a14aebed36c834040ca70ebd4f94c4f50ca3bd2c93efda5bd36194924be978701d76742f84635bea4221c', 'clientOrderId': 1, 'originator': '0x64bdd35077Ce078aC7B87Cfb381d7F50059BDb16', 'isPostOnly': False, 'quote': '0x4F96Fe3b7A6Cf9725f59d353F723c1bDb64CA6Aa', 'triggered': False, 'reduceOnly': False}]}

Cancel Order

Create an order cancel request and send it via websocket

You will need the uuid for the orders you want to cancel. Once you have the uuids of the orders, create a request body with the following payload:

request_body = [{"op": "remove", "value": uuids}]

uuids are in a list. In the example python code there is only 1 element in this list.

Then generate a request dictionary or map as follows:

request = { "method": "PATCH", "uri": "/order", "headers": {"requestid": str(uuid.uuid4())}, "body": json.dumps(request_body), "params": {"instrument": } }

Format this request using the protected_endpoint_request function in leverj.websocket.util.py and then emit the event and data to send an order cancellation request to the server.

def cancel_order(uuids, originator_credentials):
    client = Client('./resources/config/kovan.leverj.io/c21b18-64bdd3.json')
    client.set_api_url('https://kovan.leverj.io/futures/api/v1')
    all_config = client.get_all_config()
    instruments = all_config.get('instruments')
    BTCDAI_instrument = instruments.get('1')
    request_body = [{"op": "remove", "value": uuids}]
    request = {
        "method": "PATCH",
        "uri": "/order",
        "headers": {"requestid": str(uuid.uuid4())},
        "body": json.dumps(request_body),
        "params": {"instrument": BTCDAI_instrument.get('symbol')}
    }
    return request



uuids = ['9c8e9a00-a304-11eb-be7d-fc18fda03052']


request = cancel_order(uuids, originator_credentials)

# protected_endpoint_request util method adds required signature headers and formats the request
protected_request_payload = protected_endpoint_request(request, originator_credentials)
    print(protected_request_payload)

# emit from client to the connected websocket
websocket_client.sio.emit(protected_request_payload.get('event'), protected_request_payload.get('data'))


Response

Once the order is cancelled, you will receive a confirmation if you are listening to order_del

**** response ****
on_order_del: {'result': '9c8e9a00-a304-11eb-be7d-fc18fda03052'}

Position

Listen to position creation and any updates to an existing position

Make sure you are listening to position after connecting and registering successfully. You will be notified when a new position is created or any of your position parameters is modified. For example, you will get a message when your margin or ranking changes.

sio = socketio.Client(logger=False, engineio_logger=False)
sio.on("position", on_account_balance)
sio.connect('https://kovan.leverj.io', socketio_path='/futures/socket.io')

def on_position(self, data):
    print(f'on_position: {data}')

Response

If listening to position you should see information about your current positions when you receive data on this topic.

**** response ****
on_position: {'accountId': '0xc21b183A8050D1988117B86408655ff974d021A0', 'instrument': '1', 'notional': '538630000000000000000', 'margin': '269315000000000000000', 'size': '0.01', 'liquidationPrice': '27201', 'ranking': 269315000000, 'bankruptcyPrice': '26932', 'eventTime': 1619114054305, 'reservedFees': '215452000000000000'}

Liquidation

Listen to liquidation events

Make sure you are listening to liquidation after connecting and registering successfully. You will be notified when any of your positions is liquidated.

sio = socketio.Client(logger=False, engineio_logger=False)
sio.on("liquidation", on_account_balance)
sio.connect('https://kovan.leverj.io', socketio_path='/futures/socket.io')

def on_liquidation(self, data):
    print(f'on_liquidation: {data}')

Response

If listening to liquidation you should see information when positions are liquidated.

**** response ****
TODO

Auto Deleveraging (ADL)

Listen to ADL events

Make sure you are listening to adl after connecting and registering successfully. You will be notified when any of your positions is auto-deleveraged.

sio = socketio.Client(logger=False, engineio_logger=False)
sio.on("adl", on_account_balance)
sio.connect('https://kovan.leverj.io', socketio_path='/futures/socket.io')

def on_adl(self, data):
    print(f'on_adl: {data}')

Response

If listening to adl you should see information when positions are auto-deleveraged.

**** response ****
TODO

Order Execution

Listen for fills as orders are partially or fully matched

Make sure you are listening to order_execution after connecting and registering successfully.

sio = socketio.Client(logger=False, engineio_logger=False)
sio.on("order_execution", on_account_balance)
sio.connect('https://kovan.leverj.io', socketio_path='/futures/socket.io')

def on_order_execution(self, data):
    print(f'on_order_execution: {data}')

Response

If listening to order_execution you should see a message or data point every time an existing order is partially or fully matched. You could track each and every fill by listening on this topic.

Sample response shows data for a partial match. Order was for same price but for 0.04 as the quantity. 0.01 of that order was matched in this case. Use orderId to match against open orders and track fills.

**** response ****
on_order_execution: {'instrument': '1', 'accountId': '0xc21b183A8050D1988117B86408655ff974d021A0', 'executionId': 'c0120ae0-a393-11eb-be58-92c3aa195f67', 'side': 'buy', 'orderType': 'LMT', 'orderId': '97ac02b0-a391-11eb-9afb-ab76d3b77c82', 'price': 53863, 'quantity': 0.01, 'commission': 0.107726, 'pnl': 0, 'eventTime': 1619114054286342}

Account Balance

Listen to account_balance

Make sure you are listening to account_balance after connecting and registering successfully. You will receive an update on this topic whenever any of the balances changes. This happenes whenever funds are transferred in or out or deposits and withdrawals are made between the Gluon wallet and the mainnet account. It also happens when you lock funds to open orders.

sio = socketio.Client(logger=False, engineio_logger=False)
sio.on("account_balance", on_account_balance)
sio.connect('https://kovan.leverj.io', socketio_path='/futures/socket.io')

def on_account_balance(self, data):
    print(f'on_account_balance: {data}')

Response

If listening to account_balance you should see the entire set of balances when you receive data on this topic.

**** response ****
on_account_balance: {'0x4F96Fe3b7A6Cf9725f59d353F723c1bDb64CA6Aa': {'accountId': '0xc21b183A8050D1988117B86408655ff974d021A0', 'assetAddress': '0x4F96Fe3b7A6Cf9725f59d353F723c1bDb64CA6Aa', 'symbol': 'DAI', 'plasma': '17558903955615588889151', 'available': '17558903955615588889151', 'pending': '0'}, '0x6a4480B1c08A822Fed4b907AD09798ED79312a44': {'accountId': '0xc21b183A8050D1988117B86408655ff974d021A0', 'assetAddress': '0x6a4480B1c08A822Fed4b907AD09798ED79312a44', 'symbol': 'USDT', 'plasma': '16507310354', 'available': '16507310354', 'pending': '0'}, '0xF9990Bf4FFbc423b8a492771658eAade8A1E72D6': {'accountId': '0xc21b183A8050D1988117B86408655ff974d021A0', 'assetAddress': '0xF9990Bf4FFbc423b8a492771658eAade8A1E72D6', 'symbol': 'L2', 'plasma': '5500000000000000000000', 'available': '5500000000000000000000', 'pending': '0'}}

Examples (Spot)

Examples are structured as follows:

Section Description
Endpoint The /api endpoint
Client Request Request using the zka client
Server Response JSON Response payload (abbreviated to partial data sets for readability)

Exchange Basic Info

GET /all/info

curl https://live.leverj.io/spot/api/v1/all/info
response = requests.get('https://live.leverj.io/spot/api/v1/all/info')
response.json()
zka.rest.get('/all/info').then(function(result) {console.log(result)})

Response

Returns information on

for each of the spot pairs.

{ FEEETH:
   { vol24H: { qty: 0, eth: 0, instrument: 'FEEETH' },
     bid: 0.000002 },
  LEVETH:
   { vol24H: { qty: 1.2, eth: 0.0001214, instrument: 'LEVETH' },
     lastPrice: 0.000102,
     bid: 0.000093,
     ask: 0.000102 } 
}

Exchange config (format subject to frequent change)

GET /all/config

curl https://live.leverj.io/spot/api/v1/all/config
response = requests.get('https://live.leverj.io/spot/api/v1/all/config')
response.json()
zka.rest.get('/all/config').then(function(result) {console.log(result)})

Response

Returns configuration information about

{
    "config": {
        "maxInputs": 50,
        "maxOrdersCreateUpdate": 50,
        "fee": {
            "default": {
                "maker": 0,
                "taker": 0
            },
            "factor": 10000,
            "weiPerNanoFEE": 1000000,
            "DAIUSDC": {
                "maker": 0,
                "taker": 0
            }
        },
        "estimatedTokenTransferFee": "100000",
        "network": {
            "provider": "mainnet",
            "etherscan": "https://etherscan.io",
            "id": 1,
            "appId": 2,
            "gluon": "0x75ACe7a086eA0FB1a79e43Cc6331Ad053d8C67cB",
            "legacyTokensExtension": "0xDA88EfA53c85Afa30564bb651A2E76b99a232082",
            "lev": "0x0F4CA92660Efad97a9a70CB0fe969c755439772C",
            "registryLogic": "0x385827aC8d1AC7B2960D4aBc303c843D9f87Bb0C",
            "registryData": "0x0fC25C7931679B838209c484d49Df0Cb9E633C41",
            "stakeLogic": "0xe517af2457E0dD285ed22Ee4440b265f203D1B0d",
            "stakeLogicV1": "0x88Ac1E78b8a7D2B457Cb030978F71EdeE541bD5b",
            "stakeData": "0xaB3AC436D66CBEeDc734ed2c1562c3a213c9bc77",
            "spotLogic": "0x463cd03Db739B8A8c67adC8732f708A649478681",
            "spotData": "0x0d283D685F0A741C463846176e4c8EFF90D3F9EC"
        },
        "geofence": {
            "block": ["US", "PR", "AS", "VI", "GU", "UM", "SC", "CU", "SY", "SD", "IR", "KP", "UA:40", "UA:43"]
        },
        "noSignup": false,
        "maintenance": false,
        "tradingDisabled": false,
        "depositGasLimit": 160000,
        "gasPriceMultiplier": 1.5,
        "default": {
            "instrument": "LEVETH"
        },
        "minimumDepositConfirmations": 25,
        "markets": {
            "DAI": true,
            "ETH": true
        },
        "legacyTokensExtensionSupport": {
            "USDT": false
        },
        "defaultDisplayPair": "DAIUSDC"
    },
    "instruments": {
        "L2ETH": {
            "id": "L2ETH",
            "symbol": "L2ETH",
            "name": "L2/ETH",
            "status": "active",
            "ticksize": 6,
            "ticksperpoint": 1000000,
            "baseSignificantDigits": 1,
            "quoteSignificantDigits": 6,
            "base": {
                "name": "L2",
                "address": "0xBbff34E47E559ef680067a6B1c980639EEb64D24",
                "symbol": "L2",
                "decimals": 18
            },
            "quote": {
                "name": "ETH",
                "address": "0x0000000000000000000000000000000000000000",
                "symbol": "ETH",
                "decimals": 18
            },
            "convertSymbol": "ETH"
        },
        "LINKETH": {
            "id": "LINKETH",
            "symbol": "LINKETH",
            "name": "LINK/ETH",
            "status": "active",
            "ticksize": 6,
            "ticksperpoint": 1000000,
            "baseSignificantDigits": 1,
            "quoteSignificantDigits": 6,
            "base": {
                "name": "LINK",
                "address": "0x514910771AF9Ca656af840dff83E8264EcF986CA",
                "symbol": "LINK",
                "decimals": 18
            },
            "quote": {
                "name": "ETH",
                "address": "0x0000000000000000000000000000000000000000",
                "symbol": "ETH",
                "decimals": 18
            },
            "convertSymbol": "ETH"
        },
        "ETHDAI": {
            "id": "ETHDAI",
            "symbol": "ETHDAI",
            "name": "ETH/DAI",
            "status": "active",
            "ticksize": 2,
            "ticksperpoint": 100,
            "baseSignificantDigits": 4,
            "quoteSignificantDigits": 2,
            "base": {
                "name": "ETH",
                "address": "0x0000000000000000000000000000000000000000",
                "symbol": "ETH",
                "decimals": 18
            },
            "quote": {
                "name": "DAI",
                "address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
                "symbol": "DAI",
                "decimals": 18
            },
            "convertSymbol": "ETH"
        },
        "DAIUSDC": {
            "id": "DAIUSDC",
            "symbol": "DAIUSDC",
            "name": "DAI/USDC",
            "status": "active",
            "ticksize": 4,
            "ticksperpoint": 10000,
            "baseSignificantDigits": 4,
            "quoteSignificantDigits": 4,
            "base": {
                "name": "DAI",
                "address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
                "symbol": "DAI",
                "decimals": 18
            },
            "quote": {
                "name": "USDC",
                "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
                "symbol": "USDC",
                "decimals": 6
            },
            "convertSymbol": "DAI"
        }
    },
    "assets": {
        "ETH": {
            "name": "ETH",
            "address": "0x0000000000000000000000000000000000000000",
            "symbol": "ETH",
            "decimals": 18
        },
        "LINK": {
            "name": "LINK",
            "address": "0x514910771AF9Ca656af840dff83E8264EcF986CA",
            "symbol": "LINK",
            "decimals": 18
        },
        "DAI": {
            "name": "DAI",
            "address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
            "symbol": "DAI",
            "decimals": 18
        },
        "USDC": {
            "name": "USDC",
            "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
            "symbol": "USDC",
            "decimals": 6
        },
        "L2": {
            "name": "L2",
            "address": "0xBbff34E47E559ef680067a6B1c980639EEb64D24",
            "symbol": "L2",
            "decimals": 18
        }
    },
    "user": {
        "ip": "<IP Address>",
        "country": ["<country>", "<province>"]
    }
}

Recent trade data for an instrument (e.g. LEVETH)

GET /instrument/:symbol/trade

zka.rest.get('/instrument/LEVETH/trade').then(function(result) {console.log(result)})

Response

[ { date: 1550710792,
    price: 0.000102,
    volume: 0.2,
    instrument: 'LEVETH',
    side: 'buy' },
  { date: 1550708634,
    price: 0.000101,
    volume: 1,
    instrument: 'LEVETH',
    side: 'buy' },
  { date: 1550687614,
    price: 0.0001,
    volume: 1,
    instrument: 'LEVETH',
    side: 'buy' },
  { date: 1550682148,
    price: 0.0001,
    volume: 92,
    instrument: 'LEVETH',
    side: 'sell' },
  { date: 1550682147,
    price: 0.0001,
    volume: 6,
    instrument: 'LEVETH',
    side: 'buy' },
  { date: 1550682142,
    price: 0.0001,
    volume: 94,
    instrument: 'LEVETH',
    side: 'sell' } 
]

Chart for instrument

GET /instrument/:symbol/chart/:timeframe

zka.rest.get('/instrument/LEVETH/chart/5').then(function(result) {console.log(result)})

Response

[ { v: 0,
    s: 0,
    t: 1550794200,
    o: 0.000102,
    h: 0.000102,
    l: 0.000102,
    c: 0.000102,
    i: 'LEVETH' },
  { v: 0,
    s: 0,
    t: 1550793900,
    o: 0.000102,
    h: 0.000102,
    l: 0.000102,
    c: 0.000102,
    i: 'LEVETH' },
  { v: 0,
    s: 0,
    t: 1550793600,
    o: 0.000102,
    h: 0.000102,
    l: 0.000102,
    c: 0.000102,
    i: 'LEVETH' },
  { v: 0,
    s: 0,
    t: 1550793300,
    o: 0.000102,
    h: 0.000102,
    l: 0.000102,
    c: 0.000102,
    i: 'LEVETH' },
  { v: 0,
    s: 0,
    t: 1550793000,
    o: 0.000102,
    h: 0.000102,
    l: 0.000102,
    c: 0.000102,
    i: 'LEVETH' },
  { v: 0,
    s: 0,
    t: 1550792700,
    o: 0.000102,
    h: 0.000102,
    l: 0.000102,
    c: 0.000102,
    i: 'LEVETH' }
]

Order book for the instrument

GET /instrument/:symbol/orderbook

zka.rest.get('/instrument/LEVETH/orderbook').then(function(result) {console.log(result)})

Response

{ bid: 0.000093,
  ask: 0.000102,
  buy:
   [ { price: 0.000093,
       numberOfOrders: 1,
       totalQuantity: 100,
       instrument: 'LEVETH' },
     { price: 0.000092,
       numberOfOrders: 1,
       totalQuantity: 100,
       instrument: 'LEVETH' }],
  sell:
   [ { price: 0.000102,
       numberOfOrders: 1,
       totalQuantity: 0.2,
       instrument: 'LEVETH' },
     { price: 0.000103,
       numberOfOrders: 1,
       totalQuantity: 100,
       instrument: 'LEVETH' }] 
}

Get all open orders

GET /order

zka.rest.get('/order').then(function(result) {console.log(result)})

Response

[ { uuid: '77bb9a50-3639-11e9-aa67-1b934017d41a',
    accountId: '0xE239Caeb4A6eCe2567fa5307f6b5D95149a5188F',
    side: 'buy',
    quantity: 9,
    filled: 0,
    averagePrice: 0,
    cancelled: 0,
    price: 0.001209,
    entryTime: 1550795606133888,
    eventTime: 1550795606133888,
    status: 'open',
    orderType: 'LMT',
    instrument: 'LEVETH',
    timestamp: 1550795605937000,
    signature:
     '0x2de825f6315f3a712f0b69c36b690918466889edcebbd86e086c4b5e8715c9fa2bd4e74bf0cb263d2c5464b7eba5777f94246989d4f98707c9f44aa891b4743900',
    token: '0xAa7127e250E87476FdD253f15e86A4Ea9c4c4BD4',
    clientOrderId: 'ac60c4f4-bd6b-40a4-ae1d-bbef604d0bb7',
    originator: '0x12d80a4b0803Cf7D462EDF36963429B6aCfA3fFa' },
  { uuid: 'b7bf15a0-3639-11e9-b2cc-fe32bd3894ee',
    accountId: '0xE239Caeb4A6eCe2567fa5307f6b5D95149a5188F',
    side: 'buy',
    quantity: 8,
    filled: 0,
    averagePrice: 0,
    cancelled: 0,
    price: 0.001213,
    entryTime: 1550795713530585,
    eventTime: 1550795713530585,
    status: 'open',
    orderType: 'LMT',
    instrument: 'LEVETH',
    timestamp: 1550795713339000,
    signature:
     '0x962f348852da325d8663d25ee922f67b278929d500b68234ee0e294d0b4f7e601a957c8798a7fb6d26fb2bc477d3c30293790d9a64e68a3e87d6ae433432b33d01',
    token: '0xAa7127e250E87476FdD253f15e86A4Ea9c4c4BD4',
    clientOrderId: 'a0da5818-b0a3-4e39-b8eb-f5f7f7384bfe',
    originator: '0x12d80a4b0803Cf7D462EDF36963429B6aCfA3fFa' } 
]

Create New Order

POST /order

// install @leverj/adapter 
// npm i @leverj/adapter
const adapter = require("@leverj/adapter/src/OrderAdapter")

// create new order object


function createNewOrder(side, price, quantity, orderInstrument, orderAccountId, secret) {
  let order = {
    orderType: 'LMT',
    side,
    price: price.toFixed(orderInstrument.baseSignificantDigits) - 0,
    quantity: quantity.toFixed(orderInstrument.termSignificantDigits) - 0,
    timestamp: Date.now() * 1e3,
    accountId: orderAccountId,
    token: orderInstrument.address,
    instrument: orderInstrument.symbol
  }
  order.signature = adapter.sign(order, orderInstrument, secret)
  return order
}


// get instrument information for LEVETH
// create LEVETH buy order

zka.rest.get('/all/config').then(function (result) {
  const instruments = result.instruments
  const LEVETH_instrument = instruments['LEVETH']
  console.log(LEVETH_instrument)
  const newOrder = createNewOrder('buy', 0.001229, 20, LEVETH_instrument, accountId, secret)
  try {
    zka.rest.post('/order', {}, [newOrder]).catch(console.error)
  } catch (e) {
    console.error(e)
  }
})

User account

GET /account

zka.rest.get('/account').then(function(result) {console.log(result)})

Response

{ orders:
   { FEEETH: {},
     LEVETH:
      { '77bb9a50-3639-11e9-aa67-1b934017d41a': [Object],
        'b7bf15a0-3639-11e9-b2cc-fe32bd3894ee': [Object] } },
  accountId: '0xE239Caeb4A6eCe2567fa5307f6b5D95149a5188F',
  apiKeys: { '0x12d80a4b0803Cf7D462EDF36963429B6aCfA3fFa': true },
  preference: {},
  exited: {} 
}

User Executions

GET /account/execution

zka.rest.get('/account/execution').then(function(result) {console.log(result)})

Response

[ { accountId: '0xE239Caeb4A6eCe2567fa5307f6b5D95149a5188F',
    executionid: '7dd3a952-3639-11e9-bd86-99376ce577bd',
    side: 'buy',
    orderType: 'LMT',
    orderid: '7d30b290-3639-11e9-8dbe-58e2283f8f2a',
    price: 0.001228,
    quantity: 1,
    liquidity: 'commission',
    commission: 0.000002456,
    eventTime: 1550795616358063,
    instrument: 'LEVETH' },
  { accountId: '0xE239Caeb4A6eCe2567fa5307f6b5D95149a5188F',
    executionid: '7d9a22c2-3639-11e9-92a4-74585b482779',
    side: 'buy',
    orderType: 'LMT',
    orderid: '7d30b290-3639-11e9-8dbe-58e2283f8f2a',
    price: 0.001227,
    quantity: 1,
    liquidity: 'commission',
    commission: 0.000002454,
    eventTime: 1550795615980826,
    instrument: 'LEVETH' },
  { accountId: '0xE239Caeb4A6eCe2567fa5307f6b5D95149a5188F',
    executionid: '7d396520-3639-11e9-805a-71319d7431df',
    side: 'buy',
    orderType: 'LMT',
    orderid: '7d30b290-3639-11e9-8dbe-58e2283f8f2a',
    price: 0.001226,
    quantity: 1,
    liquidity: 'commission',
    commission: 0.000002452,
    eventTime: 1550795615346724,
    instrument: 'LEVETH' } 
]

Gluon

Errors

The Leverj API uses the standard HTTP error codes:

Error Code Meaning
400 Bad Request -- Usually syntax error in your HTTP request URI or headers
401 Unauthorized -- You are using an unregistered private key or your clock is way off
404 Not Found -- The specified resource could not be found
405 Method Not Allowed -- Resource does not support that method. Eg. PUT on a GET only resource
406 Not Acceptable -- You requested a format that isn't JSON
410 Gone -- Resource no longer supported
429 Too Many Requests -- You have exceed the rate limit
500 Internal Server Error -- We had a problem with our server. Try again later.
503 Service Unavailable -- We're temporarily offline for maintenance. Please try again later.