Skip to content
This repository has been archived by the owner on Aug 28, 2019. It is now read-only.

Commit

Permalink
feat: update rates regularly (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
michael1011 authored Dec 28, 2018
1 parent 4f2c608 commit 1b33081
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 39 deletions.
4 changes: 4 additions & 0 deletions bin/boltzm
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ const { argv } = require('yargs').options({
describe: 'Port of the boltz-middleware REST API',
type: 'number',
},
'api.interval': {
descibe: 'The interval at which your rates should be updated in minutes',
type: 'string',
},
'boltz.host': {
describe: 'Host of the Boltz gRPC interface',
type: 'string',
Expand Down
2 changes: 1 addition & 1 deletion lib/BoltzMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class BoltzMiddleware {
this.db = new Database(this.logger, this.config.dbpath);
this.boltzClient = new BoltzClient(this.logger, this.config.boltz);

this.service = new Service(this.logger, this.db, this.boltzClient);
this.service = new Service(this.logger, this.db, this.boltzClient, this.config.api.interval);
this.api = new Api(this.logger, this.config.api, this.service);
}

Expand Down
2 changes: 2 additions & 0 deletions lib/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ class Config {
this.api = {
host: '127.0.0.1',
port: 9001,

interval: 15,
};

this.boltz = {
Expand Down
23 changes: 18 additions & 5 deletions lib/Utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,31 +22,44 @@ export const splitPairId = (pairId: string): { quote: string, base: string } =>
};

/**
* Gets the current date in the LocaleString format.
* Get the current date in the LocaleString format.
*/
export const getTsString = (): string => (new Date()).toLocaleString('en-US', { hour12: false });

/**
* Concats an error code and its prefix
* Concat an error code and its prefix
*/
export const concatErrorCode = (prefix: number, code: number) => {
return `${prefix}.${code}`;
};

/**
* Stringifies any object or array
* Stringify any object or array
*/
export const stringify = (object: any) => {
return JSON.stringify(object, undefined, 2);
};

/**
* Turns a map into an array
* Turn a map into an array
*/
export const mapToArray = (map: Map<any, any>) => {
return Array.from(map.entries());
};

/**
* Turn a map into an object
*/
export const mapToObject = (map: Map<any, any>) => {
const object: any = {};

map.forEach((value, index) => {
object[index] = value;
});

return object;
};

/**
* Check whether a variable is a non-array object
*/
Expand All @@ -56,7 +69,7 @@ export const isObject = (val: any): boolean => {

/**
* Recursively merge properties from different sources into a target object, overriding any
* existing properties.
* existing properties
*
* @param target The destination object to merge into.
* @param sources The sources objects to copy from.
Expand Down
2 changes: 2 additions & 0 deletions lib/api/Api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import Service from '../service/Service';
type ApiConfig = {
host: string;
port: number;

interval: number;
};

class Api {
Expand Down
41 changes: 26 additions & 15 deletions lib/rates/RateProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,54 @@ import { PairInstance } from 'lib/consts/Database';
import { getPairId, stringify, mapToArray } from '../Utils';

// TODO: add unit tests
// TODO: make rate update interval configurable
class RateProvider {
private cryptoCompare: CryptoCompare;
// A map between pair ids and their rates
public rates = new Map<string, number>();

constructor(private logger: Logger) {
// A map between quote and their base assets
private baseAssetsMap = new Map<string, string[]>();

private cryptoCompare = new CryptoCompare();

constructor(private logger: Logger, private rateUpdateInterval: number) {
this.cryptoCompare = new CryptoCompare();
}

/**
* Gets a map of of rates for the provided pairs
*/
public getRates = async (pairs: PairInstance[]) => {
// A map between the quote and their base assets
const baseAssetsMap = new Map<string, string[]>();

public init = async (pairs: PairInstance[]) => {
pairs.forEach((pair) => {
const baseAssets = baseAssetsMap.get(pair.quote);
const baseAssets = this.baseAssetsMap.get(pair.quote);

if (baseAssets) {
baseAssets.push(pair.base);
} else {
baseAssetsMap.set(pair.quote, [pair.base]);
this.baseAssetsMap.set(pair.quote, [pair.base]);
}
});

this.logger.silly(`Prepared data for requests to CryptoCompare: ${stringify(mapToArray(baseAssetsMap))}`);
this.logger.silly(`Prepared data for requests to CryptoCompare: ${stringify(mapToArray(this.baseAssetsMap))}`);

await this.updateRates();

const rates = new Map<string, number>();
this.logger.silly(`Updating rates every ${this.rateUpdateInterval} minutes`);

setInterval(async () => {
await this.updateRates();
}, this.rateUpdateInterval * 60 * 1000);
}

private updateRates = async () => {
const promises: Promise<any>[] = [];

baseAssetsMap.forEach((baseAssets, quoteAsset) => {
this.baseAssetsMap.forEach((baseAssets, quoteAsset) => {
promises.push(new Promise(async (resolve) => {
const baseAssetsRates = await this.cryptoCompare.getPriceMulti(baseAssets, [quoteAsset]);

baseAssets.forEach((baseAsset) => {
rates.set(getPairId({ base: baseAsset, quote: quoteAsset }), baseAssetsRates[baseAsset][quoteAsset]);
this.rates.set(getPairId({ base: baseAsset, quote: quoteAsset }), baseAssetsRates[baseAsset][quoteAsset]);
});

resolve();
Expand All @@ -48,10 +60,9 @@ class RateProvider {

await Promise.all(promises);

this.logger.debug(`Got updated rates: ${stringify(mapToArray(rates))}`);

return rates;
this.logger.debug(`Updated rates: ${stringify(mapToArray(this.rates))}`);
}

}

export default RateProvider;
26 changes: 8 additions & 18 deletions lib/service/Service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { OrderSide, OutputType, CurrencyInfo } from '../proto/boltzrpc_pb';
import PairRepository from './PairRepository';
import RateProvider from '../rates/RateProvider';
import { PairInstance, PairFactory } from '../consts/Database';
import { splitPairId, stringify, mapToArray } from '../Utils';
import { splitPairId, stringify, mapToArray, mapToObject } from '../Utils';
import Errors from './Errors';

type PairConfig = {
Expand All @@ -19,8 +19,6 @@ type Pair = {
id: string;
base: string;
quote: string;

rate: number;
};

type PendingSwap = {
Expand All @@ -43,14 +41,10 @@ class Service extends EventEmitter {

private pairs = new Map<string, Pair>();

// This object is needed because a stringifyied Map is an empty object
// tslint:disable-next-line:no-null-keyword
private pairsObject = {};

constructor(private logger: Logger, db: Database, private boltz: BoltzClient) {
constructor(private logger: Logger, db: Database, private boltz: BoltzClient, rateInterval: number) {
super();

this.rateProvider = new RateProvider(this.logger);
this.rateProvider = new RateProvider(this.logger, rateInterval);
this.pairRepository = new PairRepository(db.models);
}

Expand Down Expand Up @@ -110,24 +104,19 @@ class Service extends EventEmitter {
}
};

const rates = await this.rateProvider.getRates(dbPairs);
await this.rateProvider.init(dbPairs);

dbPairs.forEach((pair) => {
try {
verifyBackendSupport(pair.base);
verifyBackendSupport(pair.quote);

const rate = rates.get(pair.id)!;

this.pairs.set(pair.id, {
// The values have to be set manually to avoid "TypeError: Converting circular structure to JSON" errors
rate,
id: pair.id,
base: pair.base,
quote: pair.quote,
});

this.pairsObject[pair.id] = rate;
} catch (error) {
this.logger.warn(`Could not initialise pair ${pair.id}: ${error.message}`);
}
Expand Down Expand Up @@ -158,7 +147,7 @@ class Service extends EventEmitter {
* Gets all supported pairs and their conversion rates
*/
public getPairs = () => {
return this.pairsObject;
return mapToObject(this.rateProvider.rates);
}

/**
Expand All @@ -182,12 +171,13 @@ class Service extends EventEmitter {
const { base, quote } = splitPairId(pairId);

const pair = this.pairs.get(pairId);
const rate = this.rateProvider.rates.get(pairId);

if (pair === undefined) {
if (!pair || !rate) {
throw Errors.PAIR_NOT_SUPPORTED(pairId);
}

const swapResponse = await this.boltz.createSwap(base, quote, orderSide, pair.rate, invoice, refundPublicKey, OutputType.COMPATIBILITY);
const swapResponse = await this.boltz.createSwap(base, quote, orderSide, rate, invoice, refundPublicKey, OutputType.COMPATIBILITY);
await this.boltz.listenOnAddress(this.getChainCurrency(orderSide, base, quote), swapResponse.address);

const id = uuidv4();
Expand Down

0 comments on commit 1b33081

Please sign in to comment.