diff --git a/docs/api.md b/docs/api.md index e8f0bddad..b89e95b5a 100644 --- a/docs/api.md +++ b/docs/api.md @@ -36,6 +36,8 @@ - [ListPairsResponse](#xudrpc.ListPairsResponse) - [ListPeersRequest](#xudrpc.ListPeersRequest) - [ListPeersResponse](#xudrpc.ListPeersResponse) + - [ListTradesRequest](#xudrpc.ListTradesRequest) + - [ListTradesResponse](#xudrpc.ListTradesResponse) - [LndChannels](#xudrpc.LndChannels) - [LndInfo](#xudrpc.LndInfo) - [OpenChannelRequest](#xudrpc.OpenChannelRequest) @@ -63,6 +65,7 @@ - [SubscribeSwapsRequest](#xudrpc.SubscribeSwapsRequest) - [SwapFailure](#xudrpc.SwapFailure) - [SwapSuccess](#xudrpc.SwapSuccess) + - [Trade](#xudrpc.Trade) - [UnbanRequest](#xudrpc.UnbanRequest) - [UnbanResponse](#xudrpc.UnbanResponse) - [UnlockNodeRequest](#xudrpc.UnlockNodeRequest) @@ -552,6 +555,36 @@ + + +### ListTradesRequest + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| limit | [int32](#int32) | | The maximum number of trades to return | + + + + + + + + +### ListTradesResponse + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| trades | [Trade](#xudrpc.Trade) | repeated | | + + + + + + ### LndChannels @@ -996,6 +1029,25 @@ + + +### Trade + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| maker_order | [Order](#xudrpc.Order) | | The maker order involved in this trade. | +| taker_order | [Order](#xudrpc.Order) | | The taker order involved in this trade. | +| r_hash | [string](#string) | | The payment hash involved in this trade. | +| quantity | [int64](#int64) | | The quantity transacted in this trade. | +| pair_id | [string](#string) | | The trading pair for this trade. | + + + + + + ### UnbanRequest @@ -1116,6 +1168,7 @@ | ListCurrencies | [ListCurrenciesRequest](#xudrpc.ListCurrenciesRequest) | [ListCurrenciesResponse](#xudrpc.ListCurrenciesResponse) | Gets a list of this node's supported currencies. | | ListPairs | [ListPairsRequest](#xudrpc.ListPairsRequest) | [ListPairsResponse](#xudrpc.ListPairsResponse) | Gets a list of this nodes suported trading pairs. | | ListPeers | [ListPeersRequest](#xudrpc.ListPeersRequest) | [ListPeersResponse](#xudrpc.ListPeersResponse) | Gets a list of connected peers. | +| ListTrades | [ListTradesRequest](#xudrpc.ListTradesRequest) | [ListTradesResponse](#xudrpc.ListTradesResponse) | | | PlaceOrder | [PlaceOrderRequest](#xudrpc.PlaceOrderRequest) | [PlaceOrderEvent](#xudrpc.PlaceOrderEvent) stream | Adds an order to the order book. If price is zero or unspecified a market order will get added. | | PlaceOrderSync | [PlaceOrderRequest](#xudrpc.PlaceOrderRequest) | [PlaceOrderResponse](#xudrpc.PlaceOrderResponse) | The synchronous non-streaming version of PlaceOrder. | | ExecuteSwap | [ExecuteSwapRequest](#xudrpc.ExecuteSwapRequest) | [SwapSuccess](#xudrpc.SwapSuccess) | Execute a swap on a maker peer order | diff --git a/lib/cli/commands/listtrades.ts b/lib/cli/commands/listtrades.ts new file mode 100644 index 000000000..82020865c --- /dev/null +++ b/lib/cli/commands/listtrades.ts @@ -0,0 +1,53 @@ +import { Arguments } from 'yargs'; +import { callback, loadXudClient } from '../command'; +import { ListTradesRequest, ListTradesResponse, Trade } from '../../proto/xudrpc_pb'; +import Table, { HorizontalTable } from 'cli-table3'; +import colors from 'colors/safe'; +import { satsToCoinsStr } from '../utils'; + +const HEADERS = [ + colors.blue('Trading Pair'), + colors.blue('Trade Quantity'), + colors.blue('Price'), + colors.blue('Order Type'), +]; + +const displayTrades = (trades: ListTradesResponse.AsObject) => { + const table = new Table({ head: HEADERS }) as HorizontalTable; + trades.tradesList.forEach((trade: Trade.AsObject) => { + const type = trade.makerOrder ? 'maker' : 'taker'; + let price = 0; + if (trade.makerOrder) { + price = trade.makerOrder.price; + } else if (trade.takerOrder) { + price = trade.takerOrder.price; + } + + table.push([ + trade.pairId, + satsToCoinsStr(trade.quantity), + parseFloat(price.toFixed(5)), + type, + ]); + }); + console.log(colors.underline(colors.bold('\Trades:'))); + console.log(table.toString()); +}; + +export const command = 'listtrades [limit]'; + +export const describe = 'list completed trades'; + +export const builder = { + limit: { + description: 'the maximum number of trades to display', + type: 'number', + default: 15, + }, +}; + +export const handler = (argv: Arguments) => { + const request = new ListTradesRequest(); + request.setLimit(argv.limit); + loadXudClient(argv).listTrades(request, callback(argv, displayTrades)); +}; diff --git a/lib/grpc/GrpcService.ts b/lib/grpc/GrpcService.ts index e4c8c152c..e3b50f040 100644 --- a/lib/grpc/GrpcService.ts +++ b/lib/grpc/GrpcService.ts @@ -12,6 +12,7 @@ import { errorCodes as lndErrorCodes } from '../lndclient/errors'; import { LndInfo } from '../lndclient/types'; import { SwapSuccess, SwapFailure } from '../swaps/types'; import { SwapFailureReason } from '../constants/enums'; +import { TradeInstance, OrderInstance } from '../db/types'; /** * Creates an xudrpc Order message from an [[Order]]. @@ -69,6 +70,26 @@ const createSwapFailure = (swapFailure: SwapFailure) => { return grpcSwapFailure; }; +/** + * Creates an xudrpc Order from OrderInstance. + */ +const getGrpcOrderFromOrderInstance = (order: OrderInstance) => { + const grpcOrder = new xudrpc.Order(); + grpcOrder.setCreatedAt(order.createdAt); + grpcOrder.setId(order.id); + grpcOrder.setIsOwnOrder(order.nodeId === undefined); + if (order.localId) { + grpcOrder.setLocalId(order.localId); + } + grpcOrder.setPairId(order.pairId); + // TODO: set peer pub key if order.nodeId has a value + if (order.price) { + grpcOrder.setPrice(order.price); + } + grpcOrder.setSide(order.isBuy ? xudrpc.OrderSide.BUY : xudrpc.OrderSide.SELL); + return grpcOrder; +}; + /** * Creates an xudrpc PlaceOrderResponse message from a [[PlaceOrderResult]]. */ @@ -503,6 +524,33 @@ class GrpcService { } } + /** + * See [[Service.listTrades]] + */ + public listTrades: grpc.handleUnaryCall = async (call, callback) => { + try { + const trades = await this.service.listTrades(call.request.toObject()); + const response = new xudrpc.ListTradesResponse(); + const tradesList: xudrpc.Trade[] = []; + await Promise.all(trades.map(async (trade: TradeInstance) => { + const grpcTrade = new xudrpc.Trade(); + const makerOrder = await trade.getMakerOrder(); + const takerOrder = await trade.getTakerOrder(); + grpcTrade.setQuantity(trade.quantity); + grpcTrade.setRHash(trade.rHash ? trade.rHash : ''); + grpcTrade.setMakerOrder(getGrpcOrderFromOrderInstance(makerOrder!)); + grpcTrade.setTakerOrder(takerOrder ? getGrpcOrderFromOrderInstance(takerOrder) : undefined); + grpcTrade.setPairId(makerOrder!.pairId); + tradesList.push(grpcTrade); + })); + + response.setTradesList(tradesList); + callback(null, response); + } catch (err) { + callback(this.getGrpcError(err), null); + } + } + /** * See [[Service.listPeers]] */ diff --git a/lib/orderbook/OrderBook.ts b/lib/orderbook/OrderBook.ts index fe23485a3..1e2b764f1 100644 --- a/lib/orderbook/OrderBook.ts +++ b/lib/orderbook/OrderBook.ts @@ -200,6 +200,14 @@ class OrderBook extends EventEmitter { return currencyInstance ? currencyInstance.toJSON() : undefined; } + /** + * Gets all trades or a limited number of trades from the database. + */ + public getTrades = async (limit?: number) => { + const response = await this.repository.getTrades(limit); + return response; + } + /** * Get lists of buy and sell orders of peers. */ diff --git a/lib/orderbook/OrderBookRepository.ts b/lib/orderbook/OrderBookRepository.ts index cfeb2f7c7..4374a4f73 100644 --- a/lib/orderbook/OrderBookRepository.ts +++ b/lib/orderbook/OrderBookRepository.ts @@ -49,6 +49,14 @@ class OrderbookRepository { public addTrade = (trade: db.TradeFactory) => { return this.models.Trade.create(trade); } + + public getTrades = (limit?: number): Bluebird => { + if (limit) { + return this.models.Trade.findAll({ limit, order: [['createdAt', 'DESC']] }); + } else { + return this.models.Trade.findAll({ order: [['createdAt', 'DESC']] }); + } + } } export default OrderbookRepository; diff --git a/lib/proto/xudp2p_grpc_pb.js b/lib/proto/xudp2p_grpc_pb.js index 51b4d6959..97b3a2461 100644 --- a/lib/proto/xudp2p_grpc_pb.js +++ b/lib/proto/xudp2p_grpc_pb.js @@ -1 +1 @@ -// GENERATED CODE -- NO SERVICES IN PROTO +// GENERATED CODE -- NO SERVICES IN PROTO \ No newline at end of file diff --git a/lib/proto/xudrpc.swagger.json b/lib/proto/xudrpc.swagger.json index 30e53a259..b8334716d 100644 --- a/lib/proto/xudrpc.swagger.json +++ b/lib/proto/xudrpc.swagger.json @@ -237,6 +237,32 @@ ] } }, + "/v1/listtrades": { + "post": { + "operationId": "ListTrades", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/xudrpcListTradesResponse" + } + } + }, + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/xudrpcListTradesRequest" + } + } + ], + "tags": [ + "Xud" + ] + } + }, "/v1/nodeinfo": { "get": { "summary": "Gets general information about a node.", @@ -918,6 +944,27 @@ } } }, + "xudrpcListTradesRequest": { + "type": "object", + "properties": { + "limit": { + "type": "integer", + "format": "int32", + "title": "The maximum number of trades to return" + } + } + }, + "xudrpcListTradesResponse": { + "type": "object", + "properties": { + "trades": { + "type": "array", + "items": { + "$ref": "#/definitions/xudrpcTrade" + } + } + } + }, "xudrpcLndChannels": { "type": "object", "properties": { @@ -1388,6 +1435,32 @@ } } }, + "xudrpcTrade": { + "type": "object", + "properties": { + "maker_order": { + "$ref": "#/definitions/xudrpcOrder", + "description": "The maker order involved in this trade." + }, + "taker_order": { + "$ref": "#/definitions/xudrpcOrder", + "description": "The taker order involved in this trade." + }, + "r_hash": { + "type": "string", + "description": "The payment hash involved in this trade." + }, + "quantity": { + "type": "string", + "format": "int64", + "description": "The quantity transacted in this trade." + }, + "pair_id": { + "type": "string", + "description": "The trading pair for this trade." + } + } + }, "xudrpcUnbanRequest": { "type": "object", "properties": { diff --git a/lib/proto/xudrpc_grpc_pb.d.ts b/lib/proto/xudrpc_grpc_pb.d.ts index 25eabfb05..b6cde0964 100644 --- a/lib/proto/xudrpc_grpc_pb.d.ts +++ b/lib/proto/xudrpc_grpc_pb.d.ts @@ -72,6 +72,7 @@ interface IXudService extends grpc.ServiceDefinition; responseDeserialize: grpc.deserialize; } +interface IXudService_IListTrades extends grpc.MethodDefinition { + path: string; // "/xudrpc.Xud/ListTrades" + requestStream: boolean; // false + responseStream: boolean; // false + requestSerialize: grpc.serialize; + requestDeserialize: grpc.deserialize; + responseSerialize: grpc.serialize; + responseDeserialize: grpc.deserialize; +} interface IXudService_IPlaceOrder extends grpc.MethodDefinition { path: string; // "/xudrpc.Xud/PlaceOrder" requestStream: boolean; // false @@ -318,6 +328,7 @@ export interface IXudServer { listCurrencies: grpc.handleUnaryCall; listPairs: grpc.handleUnaryCall; listPeers: grpc.handleUnaryCall; + listTrades: grpc.handleUnaryCall; placeOrder: grpc.handleServerStreamingCall; placeOrderSync: grpc.handleUnaryCall; executeSwap: grpc.handleUnaryCall; @@ -373,6 +384,9 @@ export interface IXudClient { listPeers(request: xudrpc_pb.ListPeersRequest, callback: (error: grpc.ServiceError | null, response: xudrpc_pb.ListPeersResponse) => void): grpc.ClientUnaryCall; listPeers(request: xudrpc_pb.ListPeersRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: xudrpc_pb.ListPeersResponse) => void): grpc.ClientUnaryCall; listPeers(request: xudrpc_pb.ListPeersRequest, metadata: grpc.Metadata, options: Partial, callback: (error: grpc.ServiceError | null, response: xudrpc_pb.ListPeersResponse) => void): grpc.ClientUnaryCall; + listTrades(request: xudrpc_pb.ListTradesRequest, callback: (error: grpc.ServiceError | null, response: xudrpc_pb.ListTradesResponse) => void): grpc.ClientUnaryCall; + listTrades(request: xudrpc_pb.ListTradesRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: xudrpc_pb.ListTradesResponse) => void): grpc.ClientUnaryCall; + listTrades(request: xudrpc_pb.ListTradesRequest, metadata: grpc.Metadata, options: Partial, callback: (error: grpc.ServiceError | null, response: xudrpc_pb.ListTradesResponse) => void): grpc.ClientUnaryCall; placeOrder(request: xudrpc_pb.PlaceOrderRequest, options?: Partial): grpc.ClientReadableStream; placeOrder(request: xudrpc_pb.PlaceOrderRequest, metadata?: grpc.Metadata, options?: Partial): grpc.ClientReadableStream; placeOrderSync(request: xudrpc_pb.PlaceOrderRequest, callback: (error: grpc.ServiceError | null, response: xudrpc_pb.PlaceOrderResponse) => void): grpc.ClientUnaryCall; @@ -445,6 +459,9 @@ export class XudClient extends grpc.Client implements IXudClient { public listPeers(request: xudrpc_pb.ListPeersRequest, callback: (error: grpc.ServiceError | null, response: xudrpc_pb.ListPeersResponse) => void): grpc.ClientUnaryCall; public listPeers(request: xudrpc_pb.ListPeersRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: xudrpc_pb.ListPeersResponse) => void): grpc.ClientUnaryCall; public listPeers(request: xudrpc_pb.ListPeersRequest, metadata: grpc.Metadata, options: Partial, callback: (error: grpc.ServiceError | null, response: xudrpc_pb.ListPeersResponse) => void): grpc.ClientUnaryCall; + public listTrades(request: xudrpc_pb.ListTradesRequest, callback: (error: grpc.ServiceError | null, response: xudrpc_pb.ListTradesResponse) => void): grpc.ClientUnaryCall; + public listTrades(request: xudrpc_pb.ListTradesRequest, metadata: grpc.Metadata, callback: (error: grpc.ServiceError | null, response: xudrpc_pb.ListTradesResponse) => void): grpc.ClientUnaryCall; + public listTrades(request: xudrpc_pb.ListTradesRequest, metadata: grpc.Metadata, options: Partial, callback: (error: grpc.ServiceError | null, response: xudrpc_pb.ListTradesResponse) => void): grpc.ClientUnaryCall; public placeOrder(request: xudrpc_pb.PlaceOrderRequest, options?: Partial): grpc.ClientReadableStream; public placeOrder(request: xudrpc_pb.PlaceOrderRequest, metadata?: grpc.Metadata, options?: Partial): grpc.ClientReadableStream; public placeOrderSync(request: xudrpc_pb.PlaceOrderRequest, callback: (error: grpc.ServiceError | null, response: xudrpc_pb.PlaceOrderResponse) => void): grpc.ClientUnaryCall; diff --git a/lib/proto/xudrpc_grpc_pb.js b/lib/proto/xudrpc_grpc_pb.js index 73547a466..3d92fe566 100644 --- a/lib/proto/xudrpc_grpc_pb.js +++ b/lib/proto/xudrpc_grpc_pb.js @@ -323,6 +323,28 @@ function deserialize_xudrpc_ListPeersResponse(buffer_arg) { return xudrpc_pb.ListPeersResponse.deserializeBinary(new Uint8Array(buffer_arg)); } +function serialize_xudrpc_ListTradesRequest(arg) { + if (!(arg instanceof xudrpc_pb.ListTradesRequest)) { + throw new Error('Expected argument of type xudrpc.ListTradesRequest'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_xudrpc_ListTradesRequest(buffer_arg) { + return xudrpc_pb.ListTradesRequest.deserializeBinary(new Uint8Array(buffer_arg)); +} + +function serialize_xudrpc_ListTradesResponse(arg) { + if (!(arg instanceof xudrpc_pb.ListTradesResponse)) { + throw new Error('Expected argument of type xudrpc.ListTradesResponse'); + } + return Buffer.from(arg.serializeBinary()); +} + +function deserialize_xudrpc_ListTradesResponse(buffer_arg) { + return xudrpc_pb.ListTradesResponse.deserializeBinary(new Uint8Array(buffer_arg)); +} + function serialize_xudrpc_OpenChannelRequest(arg) { if (!(arg instanceof xudrpc_pb.OpenChannelRequest)) { throw new Error('Expected argument of type xudrpc.OpenChannelRequest'); @@ -774,6 +796,17 @@ var XudService = exports.XudService = { responseSerialize: serialize_xudrpc_ListPeersResponse, responseDeserialize: deserialize_xudrpc_ListPeersResponse, }, + listTrades: { + path: '/xudrpc.Xud/ListTrades', + requestStream: false, + responseStream: false, + requestType: xudrpc_pb.ListTradesRequest, + responseType: xudrpc_pb.ListTradesResponse, + requestSerialize: serialize_xudrpc_ListTradesRequest, + requestDeserialize: deserialize_xudrpc_ListTradesRequest, + responseSerialize: serialize_xudrpc_ListTradesResponse, + responseDeserialize: deserialize_xudrpc_ListTradesResponse, + }, // Adds an order to the order book. // If price is zero or unspecified a market order will get added. placeOrder: { diff --git a/lib/proto/xudrpc_pb.d.ts b/lib/proto/xudrpc_pb.d.ts index 668c84412..205672850 100644 --- a/lib/proto/xudrpc_pb.d.ts +++ b/lib/proto/xudrpc_pb.d.ts @@ -786,6 +786,50 @@ export namespace ListPeersResponse { } } +export class ListTradesRequest extends jspb.Message { + getLimit(): number; + setLimit(value: number): void; + + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): ListTradesRequest.AsObject; + static toObject(includeInstance: boolean, msg: ListTradesRequest): ListTradesRequest.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: ListTradesRequest, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): ListTradesRequest; + static deserializeBinaryFromReader(message: ListTradesRequest, reader: jspb.BinaryReader): ListTradesRequest; +} + +export namespace ListTradesRequest { + export type AsObject = { + limit: number, + } +} + +export class ListTradesResponse extends jspb.Message { + clearTradesList(): void; + getTradesList(): Array; + setTradesList(value: Array): void; + addTrades(value?: Trade, index?: number): Trade; + + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): ListTradesResponse.AsObject; + static toObject(includeInstance: boolean, msg: ListTradesResponse): ListTradesResponse.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: ListTradesResponse, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): ListTradesResponse; + static deserializeBinaryFromReader(message: ListTradesResponse, reader: jspb.BinaryReader): ListTradesResponse; +} + +export namespace ListTradesResponse { + export type AsObject = { + tradesList: Array, + } +} + export class LndChannels extends jspb.Message { getActive(): number; setActive(value: number): void; @@ -942,6 +986,49 @@ export namespace Order { } +export class Trade extends jspb.Message { + + hasMakerOrder(): boolean; + clearMakerOrder(): void; + getMakerOrder(): Order | undefined; + setMakerOrder(value?: Order): void; + + + hasTakerOrder(): boolean; + clearTakerOrder(): void; + getTakerOrder(): Order | undefined; + setTakerOrder(value?: Order): void; + + getRHash(): string; + setRHash(value: string): void; + + getQuantity(): number; + setQuantity(value: number): void; + + getPairId(): string; + setPairId(value: string): void; + + + serializeBinary(): Uint8Array; + toObject(includeInstance?: boolean): Trade.AsObject; + static toObject(includeInstance: boolean, msg: Trade): Trade.AsObject; + static extensions: {[key: number]: jspb.ExtensionFieldInfo}; + static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo}; + static serializeBinaryToWriter(message: Trade, writer: jspb.BinaryWriter): void; + static deserializeBinary(bytes: Uint8Array): Trade; + static deserializeBinaryFromReader(message: Trade, reader: jspb.BinaryReader): Trade; +} + +export namespace Trade { + export type AsObject = { + makerOrder?: Order.AsObject, + takerOrder?: Order.AsObject, + rHash: string, + quantity: number, + pairId: string, + } +} + export class OrderUpdate extends jspb.Message { hasOrder(): boolean; diff --git a/lib/proto/xudrpc_pb.js b/lib/proto/xudrpc_pb.js index acedc1606..a262a482f 100644 --- a/lib/proto/xudrpc_pb.js +++ b/lib/proto/xudrpc_pb.js @@ -42,6 +42,8 @@ goog.exportSymbol('proto.xudrpc.ListPairsRequest', null, global); goog.exportSymbol('proto.xudrpc.ListPairsResponse', null, global); goog.exportSymbol('proto.xudrpc.ListPeersRequest', null, global); goog.exportSymbol('proto.xudrpc.ListPeersResponse', null, global); +goog.exportSymbol('proto.xudrpc.ListTradesRequest', null, global); +goog.exportSymbol('proto.xudrpc.ListTradesResponse', null, global); goog.exportSymbol('proto.xudrpc.LndChannels', null, global); goog.exportSymbol('proto.xudrpc.LndInfo', null, global); goog.exportSymbol('proto.xudrpc.OpenChannelRequest', null, global); @@ -70,6 +72,7 @@ goog.exportSymbol('proto.xudrpc.SubscribeSwapsRequest', null, global); goog.exportSymbol('proto.xudrpc.SwapFailure', null, global); goog.exportSymbol('proto.xudrpc.SwapSuccess', null, global); goog.exportSymbol('proto.xudrpc.SwapSuccess.Role', null, global); +goog.exportSymbol('proto.xudrpc.Trade', null, global); goog.exportSymbol('proto.xudrpc.UnbanRequest', null, global); goog.exportSymbol('proto.xudrpc.UnbanResponse', null, global); goog.exportSymbol('proto.xudrpc.UnlockNodeRequest', null, global); @@ -5342,6 +5345,316 @@ proto.xudrpc.ListPeersResponse.prototype.clearPeersList = function() { +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.xudrpc.ListTradesRequest = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.xudrpc.ListTradesRequest, jspb.Message); +if (goog.DEBUG && !COMPILED) { + proto.xudrpc.ListTradesRequest.displayName = 'proto.xudrpc.ListTradesRequest'; +} + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto suitable for use in Soy templates. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS. + * @param {boolean=} opt_includeInstance Whether to include the JSPB instance + * for transitional soy proto support: http://goto/soy-param-migration + * @return {!Object} + */ +proto.xudrpc.ListTradesRequest.prototype.toObject = function(opt_includeInstance) { + return proto.xudrpc.ListTradesRequest.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Whether to include the JSPB + * instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.xudrpc.ListTradesRequest} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.xudrpc.ListTradesRequest.toObject = function(includeInstance, msg) { + var f, obj = { + limit: jspb.Message.getFieldWithDefault(msg, 1, 0) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.xudrpc.ListTradesRequest} + */ +proto.xudrpc.ListTradesRequest.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.xudrpc.ListTradesRequest; + return proto.xudrpc.ListTradesRequest.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.xudrpc.ListTradesRequest} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.xudrpc.ListTradesRequest} + */ +proto.xudrpc.ListTradesRequest.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = /** @type {number} */ (reader.readInt32()); + msg.setLimit(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.xudrpc.ListTradesRequest.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.xudrpc.ListTradesRequest.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.xudrpc.ListTradesRequest} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.xudrpc.ListTradesRequest.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getLimit(); + if (f !== 0) { + writer.writeInt32( + 1, + f + ); + } +}; + + +/** + * optional int32 limit = 1; + * @return {number} + */ +proto.xudrpc.ListTradesRequest.prototype.getLimit = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); +}; + + +/** @param {number} value */ +proto.xudrpc.ListTradesRequest.prototype.setLimit = function(value) { + jspb.Message.setField(this, 1, value); +}; + + + +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.xudrpc.ListTradesResponse = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, proto.xudrpc.ListTradesResponse.repeatedFields_, null); +}; +goog.inherits(proto.xudrpc.ListTradesResponse, jspb.Message); +if (goog.DEBUG && !COMPILED) { + proto.xudrpc.ListTradesResponse.displayName = 'proto.xudrpc.ListTradesResponse'; +} +/** + * List of repeated fields within this message type. + * @private {!Array} + * @const + */ +proto.xudrpc.ListTradesResponse.repeatedFields_ = [1]; + + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto suitable for use in Soy templates. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS. + * @param {boolean=} opt_includeInstance Whether to include the JSPB instance + * for transitional soy proto support: http://goto/soy-param-migration + * @return {!Object} + */ +proto.xudrpc.ListTradesResponse.prototype.toObject = function(opt_includeInstance) { + return proto.xudrpc.ListTradesResponse.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Whether to include the JSPB + * instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.xudrpc.ListTradesResponse} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.xudrpc.ListTradesResponse.toObject = function(includeInstance, msg) { + var f, obj = { + tradesList: jspb.Message.toObjectList(msg.getTradesList(), + proto.xudrpc.Trade.toObject, includeInstance) + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.xudrpc.ListTradesResponse} + */ +proto.xudrpc.ListTradesResponse.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.xudrpc.ListTradesResponse; + return proto.xudrpc.ListTradesResponse.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.xudrpc.ListTradesResponse} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.xudrpc.ListTradesResponse} + */ +proto.xudrpc.ListTradesResponse.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = new proto.xudrpc.Trade; + reader.readMessage(value,proto.xudrpc.Trade.deserializeBinaryFromReader); + msg.addTrades(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.xudrpc.ListTradesResponse.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.xudrpc.ListTradesResponse.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.xudrpc.ListTradesResponse} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.xudrpc.ListTradesResponse.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getTradesList(); + if (f.length > 0) { + writer.writeRepeatedMessage( + 1, + f, + proto.xudrpc.Trade.serializeBinaryToWriter + ); + } +}; + + +/** + * repeated Trade trades = 1; + * @return {!Array.} + */ +proto.xudrpc.ListTradesResponse.prototype.getTradesList = function() { + return /** @type{!Array.} */ ( + jspb.Message.getRepeatedWrapperField(this, proto.xudrpc.Trade, 1)); +}; + + +/** @param {!Array.} value */ +proto.xudrpc.ListTradesResponse.prototype.setTradesList = function(value) { + jspb.Message.setRepeatedWrapperField(this, 1, value); +}; + + +/** + * @param {!proto.xudrpc.Trade=} opt_value + * @param {number=} opt_index + * @return {!proto.xudrpc.Trade} + */ +proto.xudrpc.ListTradesResponse.prototype.addTrades = function(opt_value, opt_index) { + return jspb.Message.addToRepeatedWrapperField(this, 1, opt_value, proto.xudrpc.Trade, opt_index); +}; + + +proto.xudrpc.ListTradesResponse.prototype.clearTradesList = function() { + this.setTradesList([]); +}; + + + /** * Generated by JsPbCodeGenerator. * @param {Array=} opt_data Optional initial data array, typically from a @@ -6340,6 +6653,290 @@ proto.xudrpc.Order.prototype.setHold = function(value) { +/** + * Generated by JsPbCodeGenerator. + * @param {Array=} opt_data Optional initial data array, typically from a + * server response, or constructed directly in Javascript. The array is used + * in place and becomes part of the constructed object. It is not cloned. + * If no data is provided, the constructed object will be empty, but still + * valid. + * @extends {jspb.Message} + * @constructor + */ +proto.xudrpc.Trade = function(opt_data) { + jspb.Message.initialize(this, opt_data, 0, -1, null, null); +}; +goog.inherits(proto.xudrpc.Trade, jspb.Message); +if (goog.DEBUG && !COMPILED) { + proto.xudrpc.Trade.displayName = 'proto.xudrpc.Trade'; +} + + +if (jspb.Message.GENERATE_TO_OBJECT) { +/** + * Creates an object representation of this proto suitable for use in Soy templates. + * Field names that are reserved in JavaScript and will be renamed to pb_name. + * To access a reserved field use, foo.pb_, eg, foo.pb_default. + * For the list of reserved names please see: + * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS. + * @param {boolean=} opt_includeInstance Whether to include the JSPB instance + * for transitional soy proto support: http://goto/soy-param-migration + * @return {!Object} + */ +proto.xudrpc.Trade.prototype.toObject = function(opt_includeInstance) { + return proto.xudrpc.Trade.toObject(opt_includeInstance, this); +}; + + +/** + * Static version of the {@see toObject} method. + * @param {boolean|undefined} includeInstance Whether to include the JSPB + * instance for transitional soy proto support: + * http://goto/soy-param-migration + * @param {!proto.xudrpc.Trade} msg The msg instance to transform. + * @return {!Object} + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.xudrpc.Trade.toObject = function(includeInstance, msg) { + var f, obj = { + makerOrder: (f = msg.getMakerOrder()) && proto.xudrpc.Order.toObject(includeInstance, f), + takerOrder: (f = msg.getTakerOrder()) && proto.xudrpc.Order.toObject(includeInstance, f), + rHash: jspb.Message.getFieldWithDefault(msg, 3, ""), + quantity: jspb.Message.getFieldWithDefault(msg, 4, 0), + pairId: jspb.Message.getFieldWithDefault(msg, 5, "") + }; + + if (includeInstance) { + obj.$jspbMessageInstance = msg; + } + return obj; +}; +} + + +/** + * Deserializes binary data (in protobuf wire format). + * @param {jspb.ByteSource} bytes The bytes to deserialize. + * @return {!proto.xudrpc.Trade} + */ +proto.xudrpc.Trade.deserializeBinary = function(bytes) { + var reader = new jspb.BinaryReader(bytes); + var msg = new proto.xudrpc.Trade; + return proto.xudrpc.Trade.deserializeBinaryFromReader(msg, reader); +}; + + +/** + * Deserializes binary data (in protobuf wire format) from the + * given reader into the given message object. + * @param {!proto.xudrpc.Trade} msg The message object to deserialize into. + * @param {!jspb.BinaryReader} reader The BinaryReader to use. + * @return {!proto.xudrpc.Trade} + */ +proto.xudrpc.Trade.deserializeBinaryFromReader = function(msg, reader) { + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + switch (field) { + case 1: + var value = new proto.xudrpc.Order; + reader.readMessage(value,proto.xudrpc.Order.deserializeBinaryFromReader); + msg.setMakerOrder(value); + break; + case 2: + var value = new proto.xudrpc.Order; + reader.readMessage(value,proto.xudrpc.Order.deserializeBinaryFromReader); + msg.setTakerOrder(value); + break; + case 3: + var value = /** @type {string} */ (reader.readString()); + msg.setRHash(value); + break; + case 4: + var value = /** @type {number} */ (reader.readInt64()); + msg.setQuantity(value); + break; + case 5: + var value = /** @type {string} */ (reader.readString()); + msg.setPairId(value); + break; + default: + reader.skipField(); + break; + } + } + return msg; +}; + + +/** + * Serializes the message to binary data (in protobuf wire format). + * @return {!Uint8Array} + */ +proto.xudrpc.Trade.prototype.serializeBinary = function() { + var writer = new jspb.BinaryWriter(); + proto.xudrpc.Trade.serializeBinaryToWriter(this, writer); + return writer.getResultBuffer(); +}; + + +/** + * Serializes the given message to binary data (in protobuf wire + * format), writing to the given BinaryWriter. + * @param {!proto.xudrpc.Trade} message + * @param {!jspb.BinaryWriter} writer + * @suppress {unusedLocalVariables} f is only used for nested messages + */ +proto.xudrpc.Trade.serializeBinaryToWriter = function(message, writer) { + var f = undefined; + f = message.getMakerOrder(); + if (f != null) { + writer.writeMessage( + 1, + f, + proto.xudrpc.Order.serializeBinaryToWriter + ); + } + f = message.getTakerOrder(); + if (f != null) { + writer.writeMessage( + 2, + f, + proto.xudrpc.Order.serializeBinaryToWriter + ); + } + f = message.getRHash(); + if (f.length > 0) { + writer.writeString( + 3, + f + ); + } + f = message.getQuantity(); + if (f !== 0) { + writer.writeInt64( + 4, + f + ); + } + f = message.getPairId(); + if (f.length > 0) { + writer.writeString( + 5, + f + ); + } +}; + + +/** + * optional Order maker_order = 1; + * @return {?proto.xudrpc.Order} + */ +proto.xudrpc.Trade.prototype.getMakerOrder = function() { + return /** @type{?proto.xudrpc.Order} */ ( + jspb.Message.getWrapperField(this, proto.xudrpc.Order, 1)); +}; + + +/** @param {?proto.xudrpc.Order|undefined} value */ +proto.xudrpc.Trade.prototype.setMakerOrder = function(value) { + jspb.Message.setWrapperField(this, 1, value); +}; + + +proto.xudrpc.Trade.prototype.clearMakerOrder = function() { + this.setMakerOrder(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {!boolean} + */ +proto.xudrpc.Trade.prototype.hasMakerOrder = function() { + return jspb.Message.getField(this, 1) != null; +}; + + +/** + * optional Order taker_order = 2; + * @return {?proto.xudrpc.Order} + */ +proto.xudrpc.Trade.prototype.getTakerOrder = function() { + return /** @type{?proto.xudrpc.Order} */ ( + jspb.Message.getWrapperField(this, proto.xudrpc.Order, 2)); +}; + + +/** @param {?proto.xudrpc.Order|undefined} value */ +proto.xudrpc.Trade.prototype.setTakerOrder = function(value) { + jspb.Message.setWrapperField(this, 2, value); +}; + + +proto.xudrpc.Trade.prototype.clearTakerOrder = function() { + this.setTakerOrder(undefined); +}; + + +/** + * Returns whether this field is set. + * @return {!boolean} + */ +proto.xudrpc.Trade.prototype.hasTakerOrder = function() { + return jspb.Message.getField(this, 2) != null; +}; + + +/** + * optional string r_hash = 3; + * @return {string} + */ +proto.xudrpc.Trade.prototype.getRHash = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, "")); +}; + + +/** @param {string} value */ +proto.xudrpc.Trade.prototype.setRHash = function(value) { + jspb.Message.setField(this, 3, value); +}; + + +/** + * optional int64 quantity = 4; + * @return {number} + */ +proto.xudrpc.Trade.prototype.getQuantity = function() { + return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 4, 0)); +}; + + +/** @param {number} value */ +proto.xudrpc.Trade.prototype.setQuantity = function(value) { + jspb.Message.setField(this, 4, value); +}; + + +/** + * optional string pair_id = 5; + * @return {string} + */ +proto.xudrpc.Trade.prototype.getPairId = function() { + return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 5, "")); +}; + + +/** @param {string} value */ +proto.xudrpc.Trade.prototype.setPairId = function(value) { + jspb.Message.setField(this, 5, value); +}; + + + /** * Generated by JsPbCodeGenerator. * @param {Array=} opt_data Optional initial data array, typically from a diff --git a/lib/service/Service.ts b/lib/service/Service.ts index fef4dca0b..aa0ff861b 100644 --- a/lib/service/Service.ts +++ b/lib/service/Service.ts @@ -349,6 +349,18 @@ class Service { return this.pool.listPeers(); } + /** + * Gets the list of trades. + */ + public listTrades = (args: {limit: number}) => { + const { limit } = args; + if (limit === 0) { + return this.orderBook.getTrades(); + } else { + return this.orderBook.getTrades(limit); + } + } + /** * Add an order to the order book. * If price is zero or unspecified a market order will get added. diff --git a/proto/xudrpc.proto b/proto/xudrpc.proto index 18279b8f8..df043079e 100644 --- a/proto/xudrpc.proto +++ b/proto/xudrpc.proto @@ -172,6 +172,13 @@ service Xud { }; } + rpc ListTrades(ListTradesRequest) returns (ListTradesResponse) { + option (google.api.http) = { + post: "/v1/listtrades" + body: "*" + }; + } + /* Adds an order to the order book. * If price is zero or unspecified a market order will get added. */ rpc PlaceOrder(PlaceOrderRequest) returns (stream PlaceOrderEvent) { @@ -432,6 +439,15 @@ message ListPeersResponse { repeated Peer peers = 1 [json_name = "peers"]; } +message ListTradesRequest { + // The maximum number of trades to return + int32 limit = 1 [json_name = "limit"]; +} + +message ListTradesResponse { + repeated Trade trades = 1 [json_name = "trades"]; +} + message LndChannels { // The number of active/online channels for this lnd instance that can be used for swaps. uint32 active = 1 [json_name = "active"]; @@ -476,6 +492,19 @@ message Order { uint64 hold = 10 [json_name = "hold"]; } +message Trade { + // The maker order involved in this trade. + Order maker_order = 1 [json_name = "maker_order"]; + // The taker order involved in this trade. + Order taker_order = 2 [json_name = "taker_order_id"]; + // The payment hash involved in this trade. + string r_hash = 3 [json_name = "r_hash"]; + // The quantity transacted in this trade. + int64 quantity = 4 [json_name = "quantity"]; + // The trading pair for this trade. + string pair_id = 5 [json_name = "pair_id"]; +} + message OrderUpdate { oneof order_update { // An order that was added to the order book.