UNPKG

@crypdex/hydro-sdk

Version:

Javascript SDK for the Hydro API

370 lines (333 loc) 12 kB
import { join } from 'path' import { hashPersonalMessage, ecsign, toRpcSig, toBuffer, privateToAddress } from 'ethereumjs-util' import { RequestHandler } from './RequestHandler' import { AuthError } from '../errors/errors' import { Candle } from '../models/Candle' import { Fee } from '../models/Fee' import { LockedBalance } from '../models/LockedBalance' import { Market } from '../models/Market' import { Order, Side, Status } from '../models/Order' import { Orderbook, OrderbookLevel } from '../models/Orderbook' import { OrderList } from '../models/OrderList' import { Ticker } from '../models/Ticker' import { Trade } from '../models/Trade' import { TradeList } from '../models/TradeList' import { SignatureHandler } from '../types' export interface HydroClientArgs { sign?: SignatureHandler account?: string baseUrl?: string privateKey?: string } export class HydroClient { private handler: RequestHandler private sign: SignatureHandler private constructor(handler: RequestHandler, sign: SignatureHandler) { this.handler = handler this.sign = sign } public static create({ sign, account, baseUrl, privateKey }: HydroClientArgs) { if (sign) { if (!account) { throw new Error('an account is required') } return HydroClient.withCustomAuth(sign, account, baseUrl) } if (privateKey) { return HydroClient.withPrivateKey(privateKey, baseUrl) } return HydroClient.withoutAuth(baseUrl) } /** * If you only want to make public API calls, no authentication is needed */ public static withoutAuth(baseUrl?: string): HydroClient { let sign = (message: string) => { throw new AuthError('Cannot authenticate without a private key!') } let handler = new RequestHandler(sign, undefined, baseUrl) return new HydroClient(handler, sign) } /** * Provide a private key for authentication purposes * @param privateKey A private key in hex format with the form "0x..." */ public static withPrivateKey(privateKey: string, baseUrl?: string): HydroClient { const privateKeyBuffer: Buffer = toBuffer(privateKey) as Buffer let sign = (message: string) => { // @ts-ignore: Error in the ethereumjs-util ts definition const shaMessage = hashPersonalMessage(toBuffer(message)) const ecdsaSignature = ecsign(shaMessage, privateKeyBuffer) return toRpcSig(ecdsaSignature.v, ecdsaSignature.r, ecdsaSignature.s) } let account = '0x' + (privateToAddress(privateKeyBuffer) as Buffer).toString('hex') let handler = new RequestHandler(sign, account, baseUrl) return new HydroClient(handler, sign) } /** * If you don't want to supply your private key, or want to integrate with a wallet, provide * your own function to sign messages and the account you will be using. * @param sign A function that takes the input message and signs it with the private key of the account * @param account The account that will be doing the signing */ public static withCustomAuth( sign: SignatureHandler, account: string, baseUrl?: string ): HydroClient { let handler = new RequestHandler(sign, account, baseUrl) return new HydroClient(handler, sign) } public setBaseUrl(baseUrl: string) { this.handler.baseUrl = baseUrl return this } /** * Public API Calls * * These calls do not require any authentication to complete, and will generally give you * public state about the Hydro API * * See https://docs.ddex.io/#public-rest-api */ /** * Returns all active markets * * See https://docs.ddex.io/#list-markets */ public async listMarkets(): Promise<Market[]> { const data = await this.handler.get(join('markets')) return data.markets.map((market: any) => new Market(market)) } /** * Returns a specific market * * See https://docs.ddex.io/#get-a-market * * @param marketId The id of the market, specified as a trading pair, e.g. "ZRX-ETH" */ public async getMarket(marketId: string): Promise<Market> { const data = await this.handler.get(join('markets', marketId)) return new Market(data.market) } /** * Returns tickers for all active markets * * See https://docs.ddex.io/#list-tickers */ public async listTickers(): Promise<Ticker[]> { const data = await this.handler.get(join('markets', 'tickers')) return data.tickers.map((ticker: any) => new Ticker(ticker)) } /** * Returns ticker for a specific market * * See https://docs.ddex.io/#get-a-ticker * * @param marketId The id of the market, specified as a trading pair, e.g. "ZRX-ETH" */ public async getTicker(marketId: string): Promise<Ticker> { const data = await this.handler.get(join('markets', marketId, 'ticker')) return new Ticker(data.ticker) } /** * Returns the orderbook for a specific market * * See https://docs.ddex.io/#get-orderbook * * @param marketId The id of the market, specified as a trading pair, e.g. "ZRX-ETH" * @param level (Optional) The amount of detail returned in the orderbook. Default is level ONE. */ public async getOrderbook(marketId: string, level?: OrderbookLevel): Promise<Orderbook> { const data = await this.handler.get(join('markets', marketId, 'orderbook'), { level: level }) return new Orderbook(data.orderBook, level) } /** * Returns paginated trades for a specific market * * See https://docs.ddex.io/#get-trades * * @param marketId The id of the market, specified as a trading pair, e.g. "ZRX-ETH" * @param page (Optional) Which page to return. Default is page 1. * @param perPage (Optional) How many results per page. Default is 20. */ public async listTrades(marketId: string, page?: number, perPage?: number): Promise<TradeList> { const data = await this.handler.get(join('markets', marketId, 'trades'), { page, perPage }) return new TradeList(data) } /** * Returns "candles" for building a trading chart for a specific market * * See https://docs.ddex.io/#get-candles * * @param marketId The id of the market, specified as a trading pair, e.g. "ZRX-ETH" * @param from The beginning of the time range as a UNIX timestamp * @param to The end of the time range as a UNIX timestamp * @param granularity The width of each candle in seconds */ public async listCandles( marketId: string, from: number, to: number, granularity: number ): Promise<Candle[]> { const data = await this.handler.get(join('markets', marketId, 'candles'), { from, to, granularity }) return data.candles.map((candle: any) => new Candle(candle)) } /** * Calculate an estimated fee taken by the exchange given a price and amount for an order * * See https://docs.ddex.io/#calculate-fees * * @param price The price of the order * @param amount The amount of token in the order */ public async calculateFees(price: string, amount: string, quoteSymbol?: string): Promise<Fee> { const data = await this.handler.get(join('fees'), { price, amount, quoteSymbol }) return new Fee(data) } /** * Private API Calls * * These calls require authentication, meaning you must have a valid trading address * and the ability to sign requests with that address' private key. * * See https://docs.ddex.io/#private-rest-api */ /** * Build a new order to submit to the exchange * * See https://docs.ddex.io/#build-unsigned-order * * @param marketId The id of the market, specified as a trading pair, e.g. "ZRX-ETH" * @param side Whether this is a "buy" or "sell" order * @param price The price of the order * @param amount The amount of token in the order */ public async buildOrder( marketId: string, side: Side, price: string, amount: string ): Promise<Order> { const data = await this.handler.post(join('orders', 'build'), { marketId, side, price, amount }) return new Order(data.order) } /** * Submit a signed order to the exchange * * See https://docs.ddex.io/#place-order * * @param orderId The id of a built order * @param signature String created by signing the orderId */ public async placeOrder(orderId: string, signature: string): Promise<Order> { const data = await this.handler.post(join('orders'), { orderId, signature }) return new Order(data.order) } /** * A convenience function that will build an order, sign the order, and then * immediately place the order on the system using the signing method passed * in. * * @param marketId The id of the market, specified as a trading pair, e.g. "ZRX-ETH" * @param side Whether this is a "buy" or "sell" order * @param price The price of the order * @param amount The amount of token in the order */ public async createOrder( marketId: string, side: Side, price: string, amount: string ): Promise<Order> { let order = await this.buildOrder(marketId, side, price, amount) let signature = await this.sign(order.id) return await this.placeOrder(order.id, signature) } /** * Cancel an order you have submitted to the exchange * * See https://docs.ddex.io/#cancel-order * * @param orderId The id of the order you wish to cancel */ public async cancelOrder(orderId: string): Promise<void> { await this.handler.delete(join('orders', orderId)) } /** * Return paginated orders you have submitted to the exchange * * See https://docs.ddex.io/#list-orders * * @param marketId (Optional) The id of the market, specified as a trading pair, e.g. "ZRX-ETH" * @param status (Optional) Choose between "pending" or "all" orders * @param page (Optional) Which page to return. Default is page 1. * @param perPage (Optional) How many results per page. Default is 20. */ public async listOrders( marketId?: string, status?: Status, page?: number, perPage?: number ): Promise<OrderList> { const data = await this.handler.get(join('orders'), { marketId, status, page, perPage }, true) return new OrderList(data) } /** * Return a specific order you have submitted to the exchange * * See https://docs.ddex.io/#get-order * * @param orderId The id of the order */ public async getOrder(orderId: string): Promise<Order> { const data = await this.handler.get(join('orders', orderId), {}, true) return new Order(data.order) } /** * Return paginated list of all trades you have made * * See https://docs.ddex.io/#list-account-trades * * @param marketId The id of the market, specified as a trading pair, e.g. "ZRX-ETH" * @param page (Optional) Which page to return. Default is page 1. * @param perPage (Optional) How many results per page. Default is 20. */ public async listAccountTrades( marketId: string, page?: number, perPage?: number ): Promise<TradeList> { const data = await this.handler.get( join('markets', marketId, 'trades', 'mine'), { page, perPage }, true ) return new TradeList(data) } /** * Return locked balances for each active token * * See https://docs.ddex.io/#list-locked-balances */ public async listLockedBalances(): Promise<LockedBalance[]> { const data = await this.handler.get(join('account', 'lockedBalances'), {}, true) return data.lockedBalances.map((lockedBalance: any) => new LockedBalance(lockedBalance)) } /** * Return a specific locked balance * * See https://docs.ddex.io/#get-locked-balance * * @param symbol The symbol for the token you want to see your locked balance */ public async getLockedBalance(symbol: string): Promise<LockedBalance> { const data = await this.handler.get(join('account', 'lockedBalance'), { symbol: symbol }, true) return new LockedBalance(data.lockedBalance) } }