xud
Version:
Exchange Union Daemon
226 lines (225 loc) • 12 kB
TypeScript
/// <reference types="node" />
import { EventEmitter } from 'events';
import { Models } from '../db/DB';
import { CurrencyCreationAttributes, CurrencyInstance, PairInstance } from '../db/types';
import Logger from '../Logger';
import Pool from '../p2p/Pool';
import Swaps from '../swaps/Swaps';
import { SwapSuccess } from '../swaps/types';
import TradingPair from './TradingPair';
import { OrderBookThresholds, OrderPortion, OwnLimitOrder, OwnMarketOrder, OwnOrder, Pair, PeerOrder, PlaceOrderEvent, PlaceOrderResult } from './types';
interface OrderBook {
/** Adds a listener to be called when a remote order was added. */
on(event: 'peerOrder.incoming', listener: (order: PeerOrder) => void): this;
/** Adds a listener to be called when all or part of a remote order was invalidated and removed */
on(event: 'peerOrder.invalidation', listener: (order: OrderPortion) => void): this;
/** Adds a listener to be called when all or part of a remote order was filled by an own order and removed */
on(event: 'peerOrder.filled', listener: (order: OrderPortion) => void): this;
/** Adds a listener to be called when all or part of a local order was swapped after being filled and executed remotely */
on(event: 'ownOrder.swapped', listener: (order: OrderPortion) => void): this;
/** Adds a listener to be called when all or part of a local order was filled by an own order and removed */
on(event: 'ownOrder.filled', listener: (order: OwnOrder) => void): this;
/** Adds a listener to be called when a local order was added */
on(event: 'ownOrder.added', listener: (order: OwnOrder) => void): this;
/** Adds a listener to be called when a local order was removed either manually or due to a swap initiated by a peer */
on(event: 'ownOrder.removed', listener: (order: OwnOrder) => void): this;
/** Notifies listeners that a remote order was added */
emit(event: 'peerOrder.incoming', order: PeerOrder): boolean;
/** Notifies listeners that all or part of a remote order was invalidated and removed */
emit(event: 'peerOrder.invalidation', order: OrderPortion): boolean;
/** Notifies listeners that all or part of a remote order was filled by an own order and removed */
emit(event: 'peerOrder.filled', order: OrderPortion): boolean;
/** Notifies listeners that all or part of a local order was swapped after being filled and executed remotely */
emit(event: 'ownOrder.swapped', order: OrderPortion): boolean;
/** Notifies listeners that all or part of a local order was filled by an own order and removed */
emit(event: 'ownOrder.filled', order: OwnOrder): boolean;
/** Notifies listeners that a local order was added */
emit(event: 'ownOrder.added', order: OwnOrder): boolean;
/** Notifies listeners that a local order was removed either manually or due to a swap initiated by a peer */
emit(event: 'ownOrder.removed', order: OwnOrder): boolean;
}
/**
* Represents an order book containing all orders for all active trading pairs. This encompasses
* all orders tracked locally and is the primary interface with which other modules interact with
* the order book.
*/
declare class OrderBook extends EventEmitter {
/** A map between active trading pair ids and trading pair instances. */
tradingPairs: Map<string, TradingPair>;
nomatching: boolean;
/** A map between own orders local id and their global id. */
private localIdMap;
/** A map of supported currency tickers to currency instances. */
private currencyInstances;
/** A map of supported trading pair tickers and pair database instances. */
private pairInstances;
private repository;
private thresholds;
private logger;
private nosanityswaps;
private nobalancechecks;
private strict;
private pool;
private swaps;
/** Max time for placeOrder iterations (due to swaps failures retries). */
private static readonly MAX_PLACEORDER_ITERATIONS_TIME;
/** Max time for sanity swaps to succeed. */
private static readonly MAX_SANITY_SWAP_TIME;
/** Gets an array of supported pair ids. */
get pairIds(): string[];
get currencies(): Map<string, CurrencyInstance>;
constructor({ logger, models, thresholds, pool, swaps, nosanityswaps, nobalancechecks, nomatching, strict }: {
logger: Logger;
models: Models;
thresholds: OrderBookThresholds;
pool: Pool;
swaps: Swaps;
nosanityswaps: boolean;
nobalancechecks: boolean;
nomatching?: boolean;
strict?: boolean;
});
private static createOutgoingOrder;
private checkThresholdCompliance;
/**
* Checks that a currency advertised by a peer is known to us, has a swap client identifier,
* and that their token identifier matches ours.
*/
private isPeerCurrencySupported;
private bindPool;
private bindSwaps;
/** Loads the supported pairs and currencies from the database. */
init: () => Promise<void>;
/**
* Gets all trades or a limited number of trades from the database.
*/
getTrades: (limit?: number | undefined) => Promise<import("../db/types").TradeInstance[]>;
/**
* Get lists of buy and sell orders of peers.
*/
getPeersOrders: (pairId: string) => import("./TradingPair").OrderSidesArrays<PeerOrder>;
/**
* Get lists of this node's own buy and sell orders.
*/
getOwnOrders: (pairId: string) => import("./TradingPair").OrderSidesArrays<OwnOrder>;
/** Get the trading pair instance for a given pairId, or throw an error if none exists. */
private getTradingPair;
/**
* Gets an own order by order id and pair id.
* @returns The order matching parameters, or undefined if no order could be found.
*/
getOwnOrder: (orderId: string, pairId: string) => OwnOrder;
private tryGetOwnOrder;
getPeerOrder: (orderId: string, pairId: string, peerPubKey: string) => PeerOrder;
addPair: (pair: Pair) => Promise<PairInstance>;
private addTradingPair;
addCurrency: (currency: CurrencyCreationAttributes) => Promise<void>;
removeCurrency: (currencyId: string) => Promise<void>;
removePair: (pairId: string) => Promise<void>;
placeLimitOrder: ({ order, immediateOrCancel, replaceOrderId, onUpdate }: {
order: OwnLimitOrder;
immediateOrCancel?: boolean | undefined;
replaceOrderId?: string | undefined;
onUpdate?: ((e: PlaceOrderEvent) => void) | undefined;
}) => Promise<PlaceOrderResult>;
placeMarketOrder: ({ order, onUpdate }: {
order: OwnMarketOrder;
onUpdate?: ((e: PlaceOrderEvent) => void) | undefined;
}) => Promise<PlaceOrderResult>;
/**
* Places an order in the order book. This method first attempts to match the order with existing
* orders by price and initiate swaps for any matches with peer orders. It can be called recursively
* for any portions of the order that fail swaps.
* @param order the order to place
* @param discardRemaining whether to discard any unmatched portion of the order, if `false` the
* unmatched portion will enter the order book.
* @param onUpdate a callback for when there are updates to the matching and order placement
* routine including internal matches, successful swaps, failed swaps, and remaining orders
* @param maxTime the deadline in epoch milliseconds for this method to end recursive calls
*/
private placeOrder;
/**
* Executes a swap between maker and taker orders. Emits the `peerOrder.filled` event if the swap succeeds.
* @returns A promise that resolves to a [[SwapSuccess]] once the swap is completed, throws a [[SwapFailureReason]] if it fails
*/
executeSwap: (maker: PeerOrder, taker: OwnOrder) => Promise<SwapSuccess>;
/**
* Adds an own order to the order book and broadcasts it to peers.
* Optionally removes/replaces an existing order.
* @returns false if it's a duplicated order or with an invalid pair id, otherwise true
*/
private addOwnOrder;
private persistTrade;
/**
* Adds an incoming peer order to the local order book. It timestamps the order based on when it
* enters the order book and also records its initial quantity upon being received.
* @returns `false` if it's a duplicated order or with an invalid pair id, otherwise true
*/
private addPeerOrder;
getOwnOrderByLocalId: (localId: string) => OwnOrder;
removeOwnOrders: () => {
removedOrderLocalIds: string[];
onHoldOrderLocalIds: string[];
};
/**
* Removes all or part of an order from the order book by its local id. Throws an error if the
* specified pairId is not supported or if the order to cancel could not be found.
* @param allowAsyncRemoval whether to allow an eventual async removal of the order in case
* some quantity of the order is on hold and cannot be immediately removed. If false, while some quantity of
* the order is on hold, an error will be thrown.
* @param quantityToRemove the quantity to remove from the order, if undefined then the entire
* order is removed.
* @returns an object summarizing the result of the order removal, including any quantity that
* was on hold and could not be immediately removed, the total quantity removed, and the quantity
* remaining on the order.
*/
removeOwnOrderByLocalId: (localId: string, allowAsyncRemoval?: boolean | undefined, quantityToRemove?: number | undefined) => {
removedQuantity: number;
onHoldQuantity: number;
pairId: string;
remainingQuantity: number;
};
private addOrderHold;
private removeOrderHold;
/**
* Removes all or part of an own order from the order book and broadcasts an order invalidation packet.
* @param quantityToRemove the quantity to remove from the order, if undefined then the full order is removed
* @param takerPubKey the node pub key of the taker who filled this order, if applicable
* @returns the removed portion of the order
*/
private removeOwnOrder;
/**
* Removes all or part of a peer order from the order book and emits the `peerOrder.invalidation` event.
* @param quantityToRemove the quantity to remove from the order, if undefined then the full order is removed
*/
removePeerOrder: (orderId: string, pairId: string, peerPubKey?: string | undefined, quantityToRemove?: number | undefined) => {
order: PeerOrder;
fullyRemoved: boolean;
};
private removePeerOrders;
private removePeerPair;
private checkPeerCurrencies;
/**
* Verifies the advertised trading pairs of a peer. Checks that the peer has advertised
* lnd pub keys for both the base and quote currencies for each pair, and optionally attempts a
* "sanity swap" for each currency which is a 1 satoshi for 1 satoshi swap of a given currency
* that demonstrates that we can both accept and receive payments for this peer.
* @param pairIds the list of trading pair ids to verify
*/
private verifyPeerPairs;
/**
* Send local orders to a given peer in an [[OrdersPacket].
* @param reqId the request id of a [[GetOrdersPacket]] packet that this method is responding to
* @param pairIds a list of trading pair ids, only orders belonging to one of these pairs will be sent
*/
private sendOrders;
stampOwnOrder: (order: OwnLimitOrder, replaceOrderId?: string | undefined) => OwnOrder;
private handleOrderInvalidation;
/**
* Handles a request from a peer to create a swap deal. Checks if the order for the requested swap
* is available and if a route exists to determine if the request should be accepted or rejected.
* Responds to the peer with a swap response packet containing either an accepted quantity or rejection reason.
*/
private handleSwapRequest;
}
export default OrderBook;