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
15 changes: 15 additions & 0 deletions src/integration/exchange/dto/mexc.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,18 @@ export interface MexcTrade {
isBuyerMaker: boolean;
isBestMatch: boolean;
}

export interface MexcMyTrade {
symbol: string;
id: number;
orderId: number;
price: string;
qty: string;
quoteQty: string;
commission: string;
commissionAsset: string;
time: number;
isBuyer: boolean;
isMaker: boolean;
isBestMatch: boolean;
}
27 changes: 27 additions & 0 deletions src/integration/exchange/services/mexc.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
Deposit,
DepositStatus,
MexcExchangeInfo,
MexcMyTrade,
MexcOrderBook,
MexcSymbol,
MexcTrade,
Expand Down Expand Up @@ -261,4 +262,30 @@ export class MexcService extends ExchangeService {
if (precision === 0) return 1;
return parseFloat('0.' + '0'.repeat(precision - 1) + '1');
}

async getTrades(from: string, to: string, since?: Date): Promise<Trade[]> {
const pair = await this.getPair(from, to);

const params: Record<string, string> = { symbol: pair.replace('/', '') };
if (since) params.startTime = since.getTime().toString();

const data = await this.request<MexcMyTrade[]>('GET', 'myTrades', params);

return data.map((t) => ({
id: t.id?.toString(),
info: t,
timestamp: t.time,
datetime: new Date(t.time).toISOString(),
symbol: pair,
order: t.orderId?.toString(),
type: undefined,
side: t.isBuyer ? 'buy' : 'sell',
takerOrMaker: t.isMaker ? 'maker' : 'taker',
price: parseFloat(t.price),
amount: parseFloat(t.qty),
cost: parseFloat(t.quoteQty),
fee: t.commission ? { cost: parseFloat(t.commission), currency: t.commissionAsset } : undefined,
fees: t.commission ? [{ cost: parseFloat(t.commission), currency: t.commissionAsset }] : [],
}));
}
}
45 changes: 28 additions & 17 deletions src/subdomains/supporting/payment/services/swiss-qr.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Injectable } from '@nestjs/common';
import { BadRequestException, Injectable } from '@nestjs/common';
import { I18nService } from 'nestjs-i18n';
import PDFDocument from 'pdfkit';
import { Config } from 'src/config/config';
Expand Down Expand Up @@ -59,11 +59,6 @@ export class SwissQRService {
throw new Error('PDF invoice is only available for CHF and EUR transactions');
}

// Use EUR-specific IBAN for EUR invoices
if (currency === 'EUR') {
bankInfo = { ...bankInfo, iban: 'CH8583019DFXSWISSEURX' };
}

const data = this.generateQrData(amount, currency, bankInfo, reference, request.userData);

const userLanguage = request.userData.language.symbol.toUpperCase();
Expand Down Expand Up @@ -508,67 +503,83 @@ export class SwissQRService {
}

const outputAsset = transaction.buyCrypto?.outputAsset;
const fiatAmount = transaction.buyCrypto?.inputAmount;
const quantity = transaction.buyCrypto?.outputAmount;
if (!outputAsset || fiatAmount == null || quantity == null)
throw new BadRequestException('Missing invoice information');

return {
quantity: transaction.buyCrypto?.outputAmount,
quantity,
description: {
assetDescription: outputAsset.description ?? outputAsset.name,
assetName: outputAsset.name,
assetBlockchain: outputAsset.blockchain,
},
fiatAmount: transaction.buyCrypto?.inputAmount,
fiatAmount,
...titleAndDate,
};
}

case TransactionType.SELL: {
const inputAsset = transaction.buyFiat?.cryptoInput?.asset;
const fiatAmount = transaction.buyFiat?.outputAmount;
const quantity = transaction.buyFiat?.inputAmount;
if (!inputAsset || fiatAmount == null || quantity == null)
throw new BadRequestException('Missing invoice information');

return {
quantity: transaction.buyFiat?.inputAmount,
quantity,
description: {
assetDescription: inputAsset.description ?? inputAsset.name,
assetName: inputAsset.name,
assetBlockchain: inputAsset.blockchain,
},
fiatAmount: transaction.buyFiat?.outputAmount,
fiatAmount,
...titleAndDate,
};
}

case TransactionType.SWAP: {
const sourceAsset = transaction.buyCrypto?.cryptoInput?.asset;
const targetAsset = transaction.buyCrypto?.outputAsset;
const fiatAmount = currency === 'CHF' ? transaction.buyCrypto?.amountInChf : transaction.buyCrypto?.amountInEur;
const quantity = transaction.buyCrypto?.inputAmount;
const targetAmount = transaction.buyCrypto?.outputAmount;
if (!sourceAsset || !targetAsset || fiatAmount == null || quantity == null || targetAmount == null)
throw new BadRequestException('Missing invoice information');

return {
quantity: transaction.buyCrypto?.inputAmount,
quantity,
description: {
sourceDescription: sourceAsset.description ?? sourceAsset.name,
sourceName: sourceAsset.name,
sourceBlockchain: sourceAsset.blockchain,
targetAmount: transaction.buyCrypto?.outputAmount,
targetAmount,
targetDescription: targetAsset.description ?? targetAsset.name,
targetName: targetAsset.name,
targetBlockchain: targetAsset.blockchain,
},
fiatAmount: currency === 'CHF' ? transaction.buyCrypto?.amountInChf : transaction.buyCrypto?.amountInEur,
fiatAmount,
...titleAndDate,
};
}

case TransactionType.REFERRAL: {
const targetBlockchain = transaction.refReward?.targetBlockchain;
if (!targetBlockchain) throw new Error('Missing blockchain information for referral');
if (!targetBlockchain) throw new BadRequestException('Missing invoice information');
const asset = await this.assetService.getNativeAsset(targetBlockchain);
if (!asset) throw new Error(`Native asset not found for blockchain ${targetBlockchain}`);
if (!asset) throw new BadRequestException('Missing invoice information');
const fiatAmount = currency === 'CHF' ? transaction.refReward?.amountInChf : transaction.refReward?.amountInEur;
const quantity = transaction.refReward?.outputAmount;
if (fiatAmount == null || quantity == null) throw new BadRequestException('Missing invoice information');

return {
quantity: transaction.refReward?.outputAmount,
quantity,
description: {
assetName: asset.name,
assetBlockchain: targetBlockchain,
},
fiatAmount: currency === 'CHF' ? transaction.refReward?.amountInChf : transaction.refReward?.amountInEur,
fiatAmount,
...titleAndDate,
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ export class TransactionRequestService {
.where('tr.type IN (:...types)', { types: [TransactionRequestType.SELL, TransactionRequestType.SWAP] })
.andWhere('tr.status = :status', { status: TransactionRequestStatus.CREATED })
.andWhere('tr.created > :created', { created })
.andWhere('d.blockchains = :blockchain', { blockchain })
.andWhere('d.blockchains LIKE :blockchain', { blockchain: `%${blockchain}%` })
.getRawMany<{ address: string }>()
.then((transactionRequests) => transactionRequests.map((deposit) => deposit.address));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export class PricingJuiceService extends PricingProvider implements OnModuleInit
PricingJuiceService.BTC,
];

private static readonly CONTRACT_FEE = 0.01;
private static readonly CONTRACT_FEE = 0.02;

private juiceService: JuiceService;
private krakenService: KrakenService;
Expand Down
Loading