optimization & tx cancel fix

This commit is contained in:
jejolare 2025-09-14 18:16:08 +07:00
parent 739afd03cd
commit 95ca3a9642
6 changed files with 113 additions and 19 deletions

View file

@ -8,6 +8,7 @@ import {
OrderWithUser,
} from '@/interfaces/database/modifiedRequests';
import User from '@/schemes/User.js';
import CancelTransactionBody from '@/interfaces/bodies/exchange-transactions/CancelTransactionBody.js';
import exchangeModel from '../models/ExchangeTransactions.js';
import ConfirmTransactionBody from '../interfaces/bodies/exchange-transactions/ConfirmTransactionBody.js';
import GetActiveTxByOrdersIdsBody from '../interfaces/bodies/exchange-transactions/GetActiveTxByOrdersIdsBody.js';
@ -208,7 +209,9 @@ class TransactionsController {
.flat()
.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
const connectedOrdersIDs = flatTxs.map((tx) => tx.creator === 'buy' ? tx.sell_order_id : tx.buy_order_id);
const connectedOrdersIDs = flatTxs.map((tx) =>
tx.creator === 'buy' ? tx.sell_order_id : tx.buy_order_id,
);
const uniqueConnectedOrdersIDs = [...new Set(connectedOrdersIDs)];
@ -266,6 +269,21 @@ class TransactionsController {
return res.status(500).send({ success: false, data: 'Unhandled error' });
}
}
async cancelTransaction(req: Request, res: Response) {
try {
if (!(req.body as CancelTransactionBody).transactionId) {
return res.status(400).json({ success: false, data: 'Invalid transaction data' });
}
const result = await exchangeModel.cancelTransaction(req.body as CancelTransactionBody);
return res.status(200).send(result);
} catch (error) {
console.log(error);
return res.status(500).send({ success: false, data: 'Unhandled error' });
}
}
}
const transactionsController = new TransactionsController();

View file

@ -0,0 +1,8 @@
import UserData from '../../common/UserData';
interface CancelTransactionBody {
transactionId: string;
userData: UserData;
}
export default CancelTransactionBody;

View file

@ -20,3 +20,15 @@ export interface PairWithFirstCurrency extends Pair {
export interface OrderWithUser extends Order {
user: User;
}
export interface OrderWithPair extends Order {
pair: Pair;
}
export interface PairWithCurrencies extends Pair {
first_currency: Currency;
second_currency: Currency;
}
export interface OrderWithPairAndCurrencies extends Order {
pair: PairWithCurrencies;
}

View file

@ -1,6 +1,8 @@
import Decimal from 'decimal.js';
import { Op } from 'sequelize';
import type { Transaction as SequelizeTransaction } from 'sequelize';
import CancelTransactionBody from '@/interfaces/bodies/exchange-transactions/CancelTransactionBody.js';
import sequelize from '@/sequelize.js';
import { sendDeleteOrderMessage, sendUpdatePairStatsMessage } from '../socket/main.js';
import ordersModel from './Orders.js';
import userModel from './User.js';
@ -444,6 +446,56 @@ class ExchangeModel {
}
}
async cancelTransaction(body: CancelTransactionBody) {
try {
return await sequelize.transaction(async (t) => {
const { userData } = body;
const { transactionId } = body;
const userRow = await userModel.getUserRow(userData.address);
if (!userRow) {
throw new Error('User not found.');
}
const transaction = await Transaction.findByPk(transactionId);
if (!transaction) {
return { success: false, data: "Transaction doesn't exist." };
}
const transactionOwnerOrder =
transaction.creator === 'buy'
? transaction.buy_order_id
: transaction.sell_order_id;
const ownerOrder = await Order.findByPk(transactionOwnerOrder, {
transaction: t,
lock: t.LOCK.UPDATE,
});
if (!ownerOrder) {
throw new Error('Owner order not found.');
}
if (ownerOrder.user_id !== userRow.id) {
return { success: false, data: 'You are not the creator of this transaction' };
}
if (transaction.status !== 'pending') {
return { success: false, data: 'Transaction is not pending' };
}
await this.returnTransactionAmount(transaction.id, t);
return { success: true };
});
} catch (error) {
console.log(error);
return { success: false, data: 'Internal error' };
}
}
async getActiveTxByOrdersIds(firstOrderId: number, secondOrderId: number) {
const txRow = await Transaction.findOne({
where: {

View file

@ -2,6 +2,11 @@ import { Op } from 'sequelize';
import Decimal from 'decimal.js';
import TransactionWithOrders from '@/interfaces/common/Transaction.js';
import Currency from '@/schemes/Currency.js';
import {
OrderWithAllTransactions,
OrderWithPair,
OrderWithPairAndCurrencies,
} from '@/interfaces/database/modifiedRequests.js';
import configModel from './Config.js';
import dexModel from './Dex.js';
import userModel from './User.js';
@ -337,29 +342,26 @@ class OrdersModel {
if (!userRow) throw new Error('Invalid address from token.');
const orders = await Order.findAll({
const orders = (await Order.findAll({
where: {
user_id: userRow.id,
},
order: [['timestamp', 'DESC']],
});
include: [
{
model: Pair,
as: 'pair',
include: ['first_currency', 'second_currency'],
},
],
})) as OrderWithPairAndCurrencies[];
const ordersWithCurrencies: Order[] = orders;
const result = [];
for (let i = 0; i < orders.length; i++) {
const pairData = await dexModel.getPairRow(orders[i].pair_id);
if (!pairData) throw new Error('Invalid pair id in order row.');
result.push({
...(ordersWithCurrencies[i]?.toJSON() || {}),
first_currency: await configModel.getCurrencyRow(pairData.first_currency_id),
second_currency: await configModel.getCurrencyRow(pairData.second_currency_id),
isInstant: dexModel.isBotActive(ordersWithCurrencies[i].id),
});
}
const result = orders.map((e) => ({
...e.toJSON(),
first_currency: e.pair.first_currency,
second_currency: e.pair.second_currency,
isInstant: dexModel.isBotActive(e.id),
}));
return { success: true, data: result };
} catch (err) {

View file

@ -22,4 +22,6 @@ transactionsRouter.post(
transactionsController.getPendingTransactions,
);
transactionsRouter.post('/transactions/cancel', transactionsController.cancelTransaction);
export default transactionsRouter;