@sangaman/xud
Version:
Exchange Union Daemon
260 lines • 11.7 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const events_1 = require("events");
const errors_1 = __importDefault(require("./errors"));
const packets = __importStar(require("../p2p/packets/types"));
const enums_1 = require("../types/enums");
const crypto_1 = require("crypto");
/** Functions to check argument validity and throw [[INVALID_ARGUMENT]] when invalid. */
const argChecks = {
HAS_HOST: ({ host }) => { if (host === '')
throw errors_1.default.INVALID_ARGUMENT('host must be specified'); },
HAS_ORDER_ID: ({ orderId }) => { if (orderId === '')
throw errors_1.default.INVALID_ARGUMENT('orderId must be specified'); },
HAS_NODE_PUB_KEY: ({ nodePubKey }) => {
if (nodePubKey === '')
throw errors_1.default.INVALID_ARGUMENT('nodePubKey must be specified');
},
HAS_PAIR_ID: ({ pairId }) => { if (pairId === '')
throw errors_1.default.INVALID_ARGUMENT('pairId must be specified'); },
MAX_RESULTS_NOT_NEGATIVE: ({ maxResults }) => {
if (maxResults < 0)
throw errors_1.default.INVALID_ARGUMENT('maxResults cannot be negative');
},
NON_ZERO_QUANTITY: ({ quantity }) => { if (quantity === 0)
throw errors_1.default.INVALID_ARGUMENT('quantity must not equal 0'); },
PRICE_NON_NEGATIVE: ({ price }) => { if (price < 0)
throw errors_1.default.INVALID_ARGUMENT('price cannot be negative'); },
VALID_PORT: ({ port }) => {
if (port < 1024 || port > 65535 || !Number.isInteger(port))
throw errors_1.default.INVALID_ARGUMENT('port must be an integer between 1024 and 65535');
},
};
/** Class containing the available RPC methods for XUD */
class Service extends events_1.EventEmitter {
/** Create an instance of available RPC methods and bind all exposed functions. */
constructor(logger, components) {
super();
this.logger = logger;
/*
* Cancel placed order from the orderbook.
*/
this.cancelOrder = (args) => __awaiter(this, void 0, void 0, function* () {
const { orderId, pairId } = args;
argChecks.HAS_ORDER_ID(args);
argChecks.HAS_PAIR_ID(args);
const { removed, globalId } = this.orderBook.removeOwnOrderByLocalId(pairId, orderId);
if (removed) {
this.pool.broadcastOrderInvalidation({
pairId,
orderId: globalId,
});
}
return { canceled: removed };
});
/**
* Connect to an XU node on a given host and port.
*/
this.connect = (args) => __awaiter(this, void 0, void 0, function* () {
const { host, port, nodePubKey } = args;
argChecks.HAS_NODE_PUB_KEY(args);
argChecks.HAS_HOST(args);
argChecks.VALID_PORT(args);
const peer = yield this.pool.addOutbound({ host, port }, nodePubKey);
return peer.getStatus();
});
/*
* Disconnect from a connected peer XU node on a given host and port.
*/
this.disconnect = (args) => __awaiter(this, void 0, void 0, function* () {
const { nodePubKey } = args;
argChecks.HAS_NODE_PUB_KEY(args);
yield this.pool.closePeer(nodePubKey);
return 'success';
});
/**
* Execute an atomic swap
*/
this.executeSwap = ({ targetAddress, payload }) => {
let body;
let takerPubKey;
let takerCoin;
let makerCoin;
if (!payload) {
return 'no payload provided';
}
if (targetAddress) {
return 'target address provided';
}
if (!payload.role || payload.role.toUpperCase() !== 'TAKER') {
return 'role, if provided, must be of "taker"';
}
if (!payload.receivingToken ||
(payload.receivingToken.toUpperCase() !== 'BTC' && payload.receivingToken.toUpperCase() !== 'LTC')) {
return 'receivingToken can only be LTC or BTC';
}
switch (payload.receivingToken.toUpperCase()) {
case 'BTC':
takerPubKey = this.lndBtcClient.pubKey;
takerCoin = enums_1.CurrencyType.BTC;
makerCoin = enums_1.CurrencyType.LTC;
break;
case 'LTC':
takerPubKey = this.lndLtcClient.pubKey;
takerCoin = enums_1.CurrencyType.LTC;
makerCoin = enums_1.CurrencyType.BTC;
break;
default:
return 'Invalid receiving token';
}
if (!takerPubKey) {
return 'Taker\'s LND is not connected';
}
body = {
takerCoin,
makerCoin,
takerPubKey,
takerDealId: crypto_1.randomBytes(32).toString('hex'),
takerAmount: payload.receivingAmount,
makerAmount: payload.sendingAmount,
};
const deal = {
myRole: enums_1.SwapDealRole.Taker,
takerAmount: body.takerAmount,
takerCoin: body.takerCoin,
takerPubKey: body.takerPubKey,
takerDealId: body.takerDealId,
makerAmount: body.makerAmount,
makerCoin: body.makerCoin,
createTime: Date.now(),
};
this.pool.swapDeals.add(deal);
this.logger.debug(' swap deal: ' + JSON.stringify(deal));
this.logger.debug('sending to peer ' + payload.nodePubKey + ': ' + JSON.stringify(body));
const packet = new packets.DealRequest(body);
const error = this.pool.sendToPeer(payload.nodePubKey, packet);
if (error) {
return error.message;
}
// Todo: wait for swap to complete and provide the preimage back to the caller
return 'Success';
};
/**
* Get general information about this Exchange Union node.
*/
this.getInfo = () => __awaiter(this, void 0, void 0, function* () {
const pairIds = this.orderBook.pairIds;
let peerOrdersCount = 0;
let ownOrdersCount = 0;
pairIds.forEach((pairId) => {
const peerOrders = this.orderBook.getPeerOrders(pairId, 0);
const ownOrders = this.orderBook.getOwnOrders(pairId, 0);
peerOrdersCount += Object.keys(peerOrders.buyOrders).length + Object.keys(peerOrders.sellOrders).length;
ownOrdersCount += Object.keys(ownOrders.buyOrders).length + Object.keys(ownOrders.sellOrders).length;
});
const lndbtc = this.lndBtcClient.isDisabled() ? undefined : yield this.lndBtcClient.getLndInfo();
const lndltc = this.lndLtcClient.isDisabled() ? undefined : yield this.lndLtcClient.getLndInfo();
const raiden = this.raidenClient.isDisabled() ? undefined : yield this.raidenClient.getRaidenInfo();
return {
lndbtc,
lndltc,
raiden,
version: this.version,
numPeers: this.pool.peerCount,
numPairs: pairIds.length,
orders: {
peer: peerOrdersCount,
own: ownOrdersCount,
},
};
});
/**
* Get a list of standing orders from the order book for a specified trading pair.
*/
this.getOrders = (args) => {
const { pairId, maxResults } = args;
argChecks.HAS_PAIR_ID(args);
argChecks.MAX_RESULTS_NOT_NEGATIVE(args);
const result = {
peerOrders: this.orderBook.getPeerOrders(pairId, maxResults),
ownOrders: this.orderBook.getOwnOrders(pairId, maxResults),
};
return result;
};
/**
* Get the list of the order book's available pairs.
* @returns A list of available trading pairs
*/
this.getPairs = () => {
return this.orderBook.pairs;
};
/**
* Get information about currently connected peers.
* @returns A list of connected peers with key information for each peer
*/
this.listPeers = () => {
return this.pool.listPeers();
};
/**
* Add an order to the order book.
* If price is zero or unspecified a market order will get added.
*/
this.placeOrder = (args) => __awaiter(this, void 0, void 0, function* () {
const { pairId, price, quantity, orderId } = args;
argChecks.PRICE_NON_NEGATIVE(args);
argChecks.NON_ZERO_QUANTITY(args);
argChecks.HAS_PAIR_ID(args);
const order = {
pairId,
price,
quantity,
localId: orderId,
};
return price > 0 ? this.orderBook.addLimitOrder(order) : this.orderBook.addMarketOrder(order);
});
/*
* Subscribe to incoming peer orders.
*/
this.subscribePeerOrders = (callback) => __awaiter(this, void 0, void 0, function* () {
this.orderBook.on('peerOrder.incoming', order => callback(order));
this.orderBook.on('peerOrder.invalidation', order => callback({
canceled: true,
id: order.orderId,
pairId: order.pairId,
quantity: order.quantity,
}));
});
/*
* Subscribe to executed swaps
*/
this.subscribeSwaps = (_callback) => __awaiter(this, void 0, void 0, function* () { });
this.shutdown = components.shutdown;
this.orderBook = components.orderBook;
this.lndBtcClient = components.lndBtcClient;
this.lndLtcClient = components.lndLtcClient;
this.raidenClient = components.raidenClient;
this.pool = components.pool;
this.config = components.config;
this.version = components.version;
}
}
exports.default = Service;
//# sourceMappingURL=Service.js.map