@sangaman/xud
Version:
Exchange Union Daemon
258 lines • 11.1 kB
JavaScript
;
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