UNPKG

@sangaman/xud

Version:
258 lines 11.1 kB
"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 http_1 = __importDefault(require("http")); const BaseClient_1 = __importStar(require("../BaseClient")); const errors_1 = __importDefault(require("./errors")); const utils_1 = require("../utils/utils"); /** * A utility function to parse the payload from an http response. */ function parseResponseBody(res) { return __awaiter(this, void 0, void 0, function* () { res.setEncoding('utf8'); return new Promise((resolve, reject) => { let body = ''; res.on('data', (chunk) => { body += chunk; }); res.on('end', () => { resolve(JSON.parse(body)); }); res.on('error', (err) => { reject(err); }); }); }); } /** * A class representing a client to interact with raiden. */ class RaidenClient extends BaseClient_1.default { /** * Create a raiden client. */ constructor(config, logger) { super(logger); /** Map of token swap identifiers to order ids */ this.swapIdOrderMap = new Map(); /** * Check for connectivity and get our Raiden account address */ this.init = () => __awaiter(this, void 0, void 0, function* () { if (this.isDisabled()) { this.logger.error(`can't init raiden. raiden is disabled`); return; } try { this.address = yield this.getAddress(); this.setStatus(BaseClient_1.ClientStatus.CONNECTION_VERIFIED); } catch (err) { this.logger.error('could not get raiden address', err); } }); this.getRaidenInfo = () => __awaiter(this, void 0, void 0, function* () { let channels; let address; let error; const version = 'v0.3.0'; // Hardcoded for now until they expose it to their API; if (this.isDisabled()) { error = errors_1.default.RAIDEN_IS_DISABLED.message; } else { try { channels = (yield this.getChannels()).length; address = this.address; } catch (err) { error = err.message; } } return { channels, address, error, version, }; }); /** * Send a request to the Raiden REST API. * @param endpoint the URL endpoint * @param method an HTTP request method * @param payload the request payload */ this.sendRequest = (endpoint, method, payload) => { return new Promise((resolve, reject) => { if (!this.isConnected()) { reject(errors_1.default.RAIDEN_IS_DISABLED); } const options = { method, hostname: this.host, port: this.port, path: `/api/1/${endpoint}`, }; let payloadStr; if (payload) { payloadStr = JSON.stringify(payload); options.headers = { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payloadStr), }; } const req = http_1.default.request(options, (res) => { switch (res.statusCode) { case 200: case 201: case 204: resolve(res); break; case 402: reject(errors_1.default.INSUFFICIENT_BALANCE); break; case 408: reject(errors_1.default.TIMEOUT); break; case 409: reject(errors_1.default.INVALID); break; case 500: this.logger.error(`raiden server error ${res.statusCode}: ${res.statusMessage}`); reject(errors_1.default.SERVER_ERROR); break; default: this.logger.error(`unexpected raiden status ${res.statusCode}: ${res.statusMessage}`); reject(errors_1.default.UNEXPECTED); break; } }); req.on('error', (err) => { this.logger.error(err); reject(err); }); if (payloadStr) { req.write(payloadStr); } req.end(); }); }; /** * Query for events tied to a specific channel. */ this.getChannelEvents = (channel_address) => __awaiter(this, void 0, void 0, function* () { // TODO: specify a "from_block" query argument to only get events since a specific block. const endpoint = `events/channels/${channel_address}`; const res = yield this.sendRequest(endpoint, 'GET'); return parseResponseBody(res); }); /** * Check for swap execution on a specified channel and emit swap events. */ this.checkForSwapExecution = (channel_address) => __awaiter(this, void 0, void 0, function* () { const channelEvents = yield this.getChannelEvents(channel_address); channelEvents.forEach((channelEvent) => { if (channelEvent.event_type === 'EventTransferSentSuccess' || channelEvent.event_type === 'EventTransferReceivedSuccess') { // successful swap const order = this.swapIdOrderMap.get(channelEvent.identifier); if (order) { // this matches a known order // TODO detect and specify amount swapped for partial executions, currently assume full execution this.emit('swap', Object.assign({}, order, { quantity: 0 })); } } }); }); /** * Initiates or attempts to complete a Raiden token swap * @param target_address the address of the intended swap counterparty * @param payload the token swap payload */ this.tokenSwap = (target_address, payload, order) => __awaiter(this, void 0, void 0, function* () { const identifier = utils_1.ms(); const endpoint = `token_swaps/${target_address}/${identifier}`; if (order) { this.swapIdOrderMap.set(identifier, order); } yield this.sendRequest(endpoint, 'PUT', payload); }); /** * Get info about a given raiden payment channel. * @param channel_address the address of the channel to query */ this.getChannel = (channel_address) => __awaiter(this, void 0, void 0, function* () { const endpoint = `channels/${channel_address}`; const res = yield this.sendRequest(endpoint, 'GET'); return parseResponseBody(res); }); /** * Get info about all non-settled channels. */ this.getChannels = () => __awaiter(this, void 0, void 0, function* () { const endpoint = 'channels'; const res = yield this.sendRequest(endpoint, 'GET'); return parseResponseBody(res); }); /** * Create a payment channel. * @returns The channel_address for the newly created channel. */ this.openChannel = (payload) => __awaiter(this, void 0, void 0, function* () { const endpoint = 'channels'; const res = yield this.sendRequest(endpoint, 'PUT', payload); const body = yield parseResponseBody(res); return body.channel_address; }); /** * Close a payment channel. * @param channel_address the address of the channel to close */ this.closeChannel = (channel_address) => __awaiter(this, void 0, void 0, function* () { const endpoint = `channels/${channel_address}`; const res = yield this.sendRequest(endpoint, 'PATCH', { state: 'settled' }); }); /** * Deposit more of a token to an existing channel. * @param channel_address the address of the channel to deposit to * @param balance the amount to deposit to the channel */ this.depositToChannel = (channel_address, balance) => __awaiter(this, void 0, void 0, function* () { const endpoint = `channels/${channel_address}`; const res = yield this.sendRequest(endpoint, 'PATCH', { balance }); }); /** * Get the account address for the raiden node. */ this.getAddress = () => __awaiter(this, void 0, void 0, function* () { const endpoint = `address`; const res = yield this.sendRequest(endpoint, 'GET'); const body = yield parseResponseBody(res); return body.our_address; }); const { disable, host, port } = config; this.port = port; this.host = host; if (!disable) { this.setStatus(BaseClient_1.ClientStatus.DISCONNECTED); } } } exports.default = RaidenClient; //# sourceMappingURL=RaidenClient.js.map