xud
Version:
Exchange Union Daemon
161 lines • 6.99 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
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) : adopt(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 };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createSimnetChannels = void 0;
const http_1 = __importDefault(require("http"));
const rxjs_1 = require("rxjs");
const operators_1 = require("rxjs/operators");
const command_1 = require("../cli/command");
const xudrpc_pb_1 = require("../proto/xudrpc_pb");
const processResponse = (resolve, reject) => {
return (error, response) => {
if (error) {
reject(error);
}
else {
resolve(response);
}
};
};
const getBalance = (client, currency) => __awaiter(void 0, void 0, void 0, function* () {
const request = new xudrpc_pb_1.GetBalanceRequest();
if (currency) {
request.setCurrency(currency.toUpperCase());
}
const balances = yield new Promise((resolve, reject) => {
client.getBalance(request, processResponse(resolve, reject));
});
return balances;
});
const openConnextChannel = (client, currency, amount) => __awaiter(void 0, void 0, void 0, function* () {
const request = new xudrpc_pb_1.OpenChannelRequest();
request.setCurrency(currency.toUpperCase());
request.setAmount(amount);
const openChannelResponse = yield new Promise((resolve, reject) => {
client.openChannel(request, processResponse(resolve, reject));
});
return openChannelResponse;
});
const checkBalanceObservable = (client, currency, minimumBalance, getBalance$) => {
return getBalance$.pipe(operators_1.mergeMap((balanceResponse) => {
const balancesMap = balanceResponse.getBalancesMap();
const currencyBalance = balancesMap.get(currency);
const balances = {
walletBalance: currencyBalance.getWalletBalance(),
channelBalance: currencyBalance.getChannelBalance(),
};
if (balances.walletBalance < minimumBalance) {
// the balance is under our specified threshold
// we'll hit the faucet with our connext address
// and then recheck the balance
return getConnextAddressObservable(client).pipe(operators_1.mergeMap((connextAddress) => {
return rxjs_1.from(faucetRequest(connextAddress)).pipe(
// we wait 31 seconds (~2 blocks) before checking the balance again
operators_1.delay(31000), operators_1.mergeMap(() => checkBalanceObservable(client, currency, minimumBalance, getBalance$)));
}));
}
else {
return rxjs_1.of(balances);
}
}));
};
const getInfo = (client) => __awaiter(void 0, void 0, void 0, function* () {
const request = new xudrpc_pb_1.GetInfoRequest();
const info = yield new Promise((resolve, reject) => {
client.getInfo(request, processResponse(resolve, reject));
});
return info;
});
const getConnextAddressObservable = (client) => {
return rxjs_1.from(getInfo(client)).pipe(operators_1.mergeMap((info) => {
const connext = info.getConnext();
if (connext && connext.getAddress()) {
return rxjs_1.of(connext.getAddress());
}
return rxjs_1.throwError('connext address not found');
}), operators_1.catchError((_error, caught) => {
return caught.pipe(operators_1.delay(5000));
}));
};
const faucetRequest = (connextAddress) => {
return new Promise((resolve, reject) => {
const options = {
method: 'POST',
hostname: 'xud1.simnet.exchangeunion.com',
port: 9000,
path: '/faucet',
};
const payload = {
address: connextAddress,
};
const 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:
resolve(res);
break;
default:
reject('faucet request failed');
break;
}
});
req.on('error', reject);
if (payloadStr) {
req.write(payloadStr);
}
req.end();
});
};
const createSimnetChannel = ({ client, currency, minChannelAmount, channelAmount, retryInterval, getBalance$, }) => {
const balances$ = checkBalanceObservable(client, currency, channelAmount, getBalance$);
const simnetChannel$ = balances$.pipe(operators_1.mergeMap((balances) => {
if (balances.channelBalance >= minChannelAmount) {
// in case we already have enough channelBalance we won't attempt
// to open a channel
return rxjs_1.empty();
}
else {
return rxjs_1.from(openConnextChannel(client, currency, channelAmount)).pipe(operators_1.mapTo(currency));
}
}),
// when error happens
operators_1.retryWhen(errors => errors.pipe(
// we wait for retryInterval and attempt again
operators_1.delay(retryInterval),
// for a maximum of 10 times
operators_1.take(10),
// until we give up completely
operators_1.concat(rxjs_1.throwError('unrecoverable error happened - giving up')))),
// complete the observable when the flow is successful
operators_1.take(1));
return simnetChannel$;
};
const createSimnetChannels = (config) => {
const client$ = rxjs_1.defer(() => rxjs_1.from(command_1.loadXudClient({}))).pipe(operators_1.share());
return client$.pipe(operators_1.mergeMap((client) => {
const getBalance$ = rxjs_1.defer(() => rxjs_1.from(getBalance(client))).pipe(operators_1.share());
return rxjs_1.from(
// we map our channels config into observables
config.channels.map(channelConfig => createSimnetChannel(Object.assign(Object.assign({}, channelConfig), { client,
getBalance$, retryInterval: config.retryInterval })))).pipe(
// execute them in order
operators_1.concatAll());
}));
};
exports.createSimnetChannels = createSimnetChannels;
//# sourceMappingURL=simnet-connext-channels.js.map