Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions migration/1774000000000-AddAvailableAmountToLiquidityBalance.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module.exports = class AddAvailableAmountToLiquidityBalance1774000000000 {
name = 'AddAvailableAmountToLiquidityBalance1774000000000';

async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "dbo"."liquidity_balance" ADD "availableAmount" float`);
}

async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "dbo"."liquidity_balance" DROP COLUMN "availableAmount"`);
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export class ExchangeController {
@ApiExcludeEndpoint()
@UseGuards(AuthGuard(), RoleGuard(UserRole.ADMIN), UserActiveGuard())
async getBalance(@Param('exchange') exchange: string): Promise<Balances> {
return this.call(exchange, (e) => e.getBalances());
return this.call(exchange, (e) => e.getRawBalances());
}

@Get(':exchange/price')
Expand Down
23 changes: 15 additions & 8 deletions src/integration/exchange/services/exchange.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { BadRequestException, Inject, OnModuleInit } from '@nestjs/common';
import BigNumber from 'bignumber.js';
import {
Balance,
Balances,
ConstructorArgs,
Dictionary,
Expand Down Expand Up @@ -67,24 +68,30 @@ export abstract class ExchangeService extends PricingProvider implements OnModul
return this.exchange.name;
}

async getBalances(): Promise<Balances> {
async getRawBalances(): Promise<Balances> {
return this.callApi((e) => e.fetchBalance());
}

async getTotalBalances(): Promise<Dictionary<number>> {
const balances = await this.getBalances().then((b) => b.total);
async getBalances(): Promise<{ total: Dictionary<number>; available: Dictionary<number> }> {
const balances = await this.getRawBalances();

const totalBalances = {};
return {
total: this.aggregateBalances(balances.total),
available: this.aggregateBalances(balances.free),
};
}

private aggregateBalances(balances: Balance): Dictionary<number> {
const result: Dictionary<number> = {};
for (const [asset, amount] of Object.entries(balances)) {
const [base, suffix] = asset.split('.');
if (!suffix || suffix === 'F') totalBalances[base] = (totalBalances[base] ?? 0) + amount;
if (!suffix || suffix === 'F') result[base] = (result[base] ?? 0) + (amount as number);
}

return totalBalances;
return result;
}

async getAvailableBalance(currency: string): Promise<number> {
return this.getBalances().then((b) => b.free[currency] ?? 0);
return this.getRawBalances().then((b) => b.free[currency] ?? 0);
}

async getPrice(from: string, to: string): Promise<Price> {
Expand Down
19 changes: 14 additions & 5 deletions src/integration/exchange/services/scrypt.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,22 +70,31 @@ export class ScryptService extends PricingProvider {

// --- BALANCES --- //

async getTotalBalances(): Promise<Record<string, number>> {
async getBalances(): Promise<{ total: Record<string, number>; available: Record<string, number> }> {
const balances = await this.balances;

const totalBalances: Record<string, number> = {};
const total: Record<string, number> = {};
const available: Record<string, number> = {};

for (const balance of balances.values()) {
totalBalances[balance.Currency] = parseFloat(balance.Amount) || 0;
const amount = parseFloat(balance.Amount) || 0;
const availableAmount = parseFloat(balance.AvailableAmount) || amount;

total[balance.Currency] = amount;
available[balance.Currency] = availableAmount;
}

return totalBalances;
return { total, available };
}

async getAvailableBalance(currency: string): Promise<number> {
const balances = await this.balances;

const balance = balances.get(currency);
return balance ? parseFloat(balance.AvailableAmount) || 0 : 0;
if (!balance) return 0;

const amount = parseFloat(balance.Amount) || 0;
return parseFloat(balance.AvailableAmount) || amount;
}

// --- WITHDRAWALS --- //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,14 @@ export class ExchangeAdapter implements LiquidityBalanceIntegration {
async getForExchange(exchange: string, assets: LiquidityManagementAsset[]): Promise<LiquidityBalance[]> {
try {
const exchangeService = this.exchangeRegistry.getExchange(exchange);
const balances = await exchangeService.getTotalBalances();
const { total: totalBalances, available: availableBalances } = await exchangeService.getBalances();

return assets.map((a) => {
const names = [a.dexName, ...(this.ASSET_MAPPINGS[a.dexName] ?? [])];
const balance = Util.sum(names.map((n) => balances[n] ?? 0));
const total = Util.sum(names.map((n) => totalBalances[n] ?? 0));
const available = Util.sum(names.map((n) => availableBalances[n] ?? 0));

return LiquidityBalance.create(a, balance);
return LiquidityBalance.create(a, total, available);
});
} catch (e) {
this.logger.error(`Failed to update liquidity management balance for ${exchange}:`, e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,29 @@ export class LiquidityBalance extends IEntity {
@Column({ type: 'float', nullable: true })
amount?: number;

@Column({ type: 'float', nullable: true })
availableAmount?: number;

@Column({ default: true })
isDfxOwned: boolean;

// --- FACTORY METHODS --- //

static create(target: Asset, amount: number): LiquidityBalance {
static create(target: Asset, amount: number, availableAmount?: number): LiquidityBalance {
const balance = new LiquidityBalance();

balance.asset = target;
balance.amount = amount;
balance.availableAmount = availableAmount ?? amount;

return balance;
}

// --- PUBLIC API --- //

updateBalance(amount: number): this {
updateBalance(amount: number, availableAmount?: number): this {
this.amount = amount;
this.availableAmount = availableAmount ?? amount;

return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export class LiquidityManagementBalanceService implements OnModuleInit {
if (existingBalance) {
if (existingBalance.updated > startDate) continue;

existingBalance.updateBalance(balance.amount ?? 0);
existingBalance.updateBalance(balance.amount ?? 0, balance.availableAmount);
await this.balanceRepo.save(existingBalance);

continue;
Expand Down
4 changes: 2 additions & 2 deletions src/subdomains/supporting/log/log-job.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -452,8 +452,8 @@ export class LogJobService {

const manualLiqPosition = manualLiqPositions.find((p) => p.assetId === curr.id)?.value ?? 0;

// plus
const liquidity = (curr.balance?.amount ?? 0) + (paymentDepositBalance ?? 0) + (manualLiqPosition ?? 0);
// plus (use availableAmount to avoid double-counting with pending exchange orders)
const liquidity = (curr.balance?.availableAmount ?? 0) + (paymentDepositBalance ?? 0) + (manualLiqPosition ?? 0);

const cryptoInput = [Blockchain.MONERO, Blockchain.LIGHTNING, Blockchain.ZANO].includes(curr.blockchain)
? 0
Expand Down
Loading