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

Commit

Permalink
feat: add fee column to database
Browse files Browse the repository at this point in the history
  • Loading branch information
michael1011 committed Mar 16, 2019
1 parent 71f92ac commit 0a171a0
Show file tree
Hide file tree
Showing 15 changed files with 232 additions and 73 deletions.
1 change: 1 addition & 0 deletions lib/BoltzMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class BoltzMiddleware {

this.notifications = new NotificationProvider(
this.logger,
this.service,
this.boltzClient,
this.config.notification,
this.config.currencies,
Expand Down
3 changes: 2 additions & 1 deletion lib/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import toml from 'toml';
import { Arguments } from 'yargs';
import { ApiConfig } from './api/Api';
import { PairConfig } from './service/Service';
import { CurrencyConfig } from './consts/Types';
import { BoltzConfig } from './boltz/BoltzClient';
import { getServiceDir, deepMerge, resolveHome } from './Utils';
import { NotificationConfig, CurrencyConfig } from './notifications/NotificationProvider';
import { NotificationConfig } from './notifications/NotificationProvider';

class Config {
public logpath: string;
Expand Down
29 changes: 28 additions & 1 deletion lib/Utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import os from 'os';
import path from 'path';
import { PairFactory } from './consts/Database';
import { GetBalanceResponse, Balance, OrderSide } from './proto/boltzrpc_pb';

const idPossibilities = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';

Expand Down Expand Up @@ -160,7 +161,7 @@ export const minutesToMilliseconds = (minutes: number) => {
/**
* Convert satoshis to whole coins and remove trailing zeros
*/
export const satoshisToWholeCoins = (satoshis: number) => {
export const satoshisToCoins = (satoshis: number) => {
return roundToDecimals(satoshis / 100000000, 8);
};

Expand All @@ -170,3 +171,29 @@ export const satoshisToWholeCoins = (satoshis: number) => {
export const roundToDecimals = (number: number, decimals: number) => {
return Number(number.toFixed(decimals));
};

/**
* Converts a "GetBalanceResponse" into a map
*/
export const parseBalances = async (balance: GetBalanceResponse.AsObject) => {
const balances = new Map<string, Balance.AsObject>();

balance.balancesMap.forEach(([key, value]) => {
balances.set(key, value);
});

return balances;
};

/**
* Gets the symbol for the fee of a swap
*/
export const getFeeSymbol = (pairId: string, orderSide: OrderSide, isReverse: boolean): string => {
const { base, quote } = splitPairId(pairId);

if (isReverse) {
return orderSide === OrderSide.BUY ? quote : base;
} else {
return orderSide === OrderSide.BUY ? base : quote;
}
};
5 changes: 4 additions & 1 deletion lib/consts/Database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,12 @@ export type PairAttributes = PairFactory & {

export type PairInstance = PairAttributes & Sequelize.Instance<PairAttributes>;

type Swap = {
export type Swap = {
id: string;
fee: number;

pair: string;
orderSide: number;
status?: string;
invoice: string;
};
Expand Down
10 changes: 10 additions & 0 deletions lib/consts/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,13 @@ export type SwapUpdate = {

preimage?: string;
};

export type CurrencyConfig = {
symbol: string;

maxSwapAmount: number;
minSwapAmount: number;

minWalletBalance: number;
minChannelBalance: number;
};
2 changes: 2 additions & 0 deletions lib/db/models/ReverseSwap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import * as db from '../../consts/Database';
export default (sequelize: Sequelize.Sequelize, dataTypes: Sequelize.DataTypes) => {
const attributes: db.SequelizeAttributes<db.ReverseSwapAttributes> = {
id: { type: dataTypes.STRING, primaryKey: true, allowNull: false },
fee: { type: dataTypes.INTEGER, allowNull: false },
pair: { type: dataTypes.STRING, allowNull: false },
orderSide: { type: dataTypes.INTEGER, allowNull: false },
status: { type: dataTypes.STRING, allowNull: true },
invoice: { type: dataTypes.STRING, allowNull: false },
preimage: { type: dataTypes.STRING, allowNull: true },
Expand Down
2 changes: 2 additions & 0 deletions lib/db/models/Swap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import * as db from '../../consts/Database';
export default (sequelize: Sequelize.Sequelize, dataTypes: Sequelize.DataTypes) => {
const attributes: db.SequelizeAttributes<db.SwapAttributes> = {
id: { type: dataTypes.STRING, primaryKey: true, allowNull: false },
fee: { type: dataTypes.INTEGER, allowNull: false },
pair: { type: dataTypes.STRING, allowNull: false },
orderSide: { type: dataTypes.INTEGER, allowNull: false },
status: { type: dataTypes.STRING, allowNull: true },
invoice: { type: dataTypes.STRING, unique: true, allowNull: false },
lockupAddress: { type: dataTypes.STRING, allowNull: false },
Expand Down
89 changes: 89 additions & 0 deletions lib/notifications/CommandHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import Service from '../service/Service';
import BoltzClient from '../boltz/BoltzClient';
import { SwapUpdateEvent } from '../consts/Enums';
import DiscordClient, { Command } from './DiscordClient';
import { satoshisToCoins, parseBalances, getFeeSymbol } from '../Utils';
import { SwapInstance, ReverseSwapInstance, Swap } from '../consts/Database';

class CommandHandler {
constructor(
private service: Service,
private boltz: BoltzClient,
private discord: DiscordClient) {

this.discord.on('command', async (command: Command) => {
switch (command) {
case Command.GetBalance:
await this.sendBalance();
break;

case Command.GetFees:
await this.sendFees();
break;
}
});
}

private sendBalance = async () => {
const balances = await parseBalances(await this.boltz.getBalance());

let message = 'Balances:';

balances.forEach((balance, symbol) => {
// tslint:disable-next-line:prefer-template
message += `\n\n**${symbol}**\n` +
`Wallet: ${satoshisToCoins(balance.walletBalance!.totalBalance)} ${symbol}\n` +
`Channels: ${satoshisToCoins(balance.channelBalance)} ${symbol}`;
});

await this.discord.sendMessage(message);
}

private sendFees = async () => {
let message = 'Fees:\n';

// Get all successful (reverse) swaps
const [swaps, reverseSwaps] = await Promise.all([
this.service.swapRepository.getSwaps({
status: SwapUpdateEvent.InvoicePaid,
}),
this.service.reverseSwapRepository.getReverseSwaps({
status: SwapUpdateEvent.InvoiceSettled,
}),
]);

const fees = this.getFeeFromSwaps(swaps, reverseSwaps);

fees.forEach((fee, symbol) => {
message += `\n**${symbol}**: ${satoshisToCoins(fee)} ${symbol}`;
});

await this.discord.sendMessage(message);
}

private getFeeFromSwaps = (swaps: SwapInstance[], reverseSwaps: ReverseSwapInstance[]): Map<string, number> => {
// A map between the symbols of the currencies and the fees collected on that chain
const fees = new Map<string, number>();

const getFeeFromSwapMap = (array: Swap[], isReverse: boolean) => {
array.forEach((swap) => {
const feeSymbol = getFeeSymbol(swap.pair, swap.orderSide, isReverse);

const fee = fees.get(feeSymbol);

if (fee) {
fees.set(feeSymbol, fee + swap.fee);
} else {
fees.set(feeSymbol, swap.fee);
}
});
};

getFeeFromSwapMap(swaps, false);
getFeeFromSwapMap(reverseSwaps, true);

return fees;
}
}

export default CommandHandler;
10 changes: 9 additions & 1 deletion lib/notifications/DiscordClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Client, TextChannel, Message } from 'discord.js';

enum Command {
GetBalance,
GetFees,
}

interface DiscordClient {
Expand All @@ -25,6 +26,10 @@ class DiscordClient extends EventEmitter {
}

public init = async () => {
if (this.token === '') {
throw 'no API token provided';
}

await this.client.login(this.token);

const { channels } = this.client;
Expand Down Expand Up @@ -66,11 +71,14 @@ class DiscordClient extends EventEmitter {
}
}

private parseMessage = (message: string) => {
private parseMessage = (message: string): Command | undefined => {
switch (message.toLowerCase()) {
case 'getbalance':
return Command.GetBalance;

case 'getfees':
return Command.GetFees;

default:
return undefined;
}
Expand Down
Loading

0 comments on commit 0a171a0

Please sign in to comment.