error handling

Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>
This commit is contained in:
microwavedcola1 2021-09-26 12:48:35 +02:00
parent 096ddee727
commit 4019b622af
6 changed files with 283 additions and 208 deletions

View File

@ -5,6 +5,7 @@ import {
} from "@blockworks-foundation/mango-client";
import BN from "bn.js";
import Controller from "controller.interface";
import { RequestErrorCustom } from "dtos";
import { NextFunction, Request, Response, Router } from "express";
import MangoSimpleClient from "mango.simple.client";
@ -26,6 +27,18 @@ class AccountController implements Controller {
response: Response,
next: NextFunction
) => {
this.fetchPerpPositionsInternal()
.then((postionDtos) => {
response.send({ success: true, result: postionDtos } as PositionsDto);
})
.catch((error) => {
return response.status(500).send({
errors: [{ msg: error.message } as RequestErrorCustom],
});
});
};
private async fetchPerpPositionsInternal() {
const groupConfig = this.mangoSimpleClient.mangoGroupConfig;
const mangoGroup = this.mangoSimpleClient.mangoGroup;
@ -133,9 +146,8 @@ class AccountController implements Controller {
} as PositionDto;
}
);
response.send({ success: true, result: postionDtos } as PositionsDto);
};
return postionDtos;
}
}
export default AccountController;

View File

@ -5,6 +5,6 @@ export interface BadRequestError {
location: string;
}
export interface BadRequestErrorCustom {
export interface RequestErrorCustom {
msg: string;
}

View File

@ -51,7 +51,7 @@ class MangoSimpleClient {
}
static async create() {
const groupName = "mainnet.1";
const groupName = process.env.GROUP_NAME || "mainnet.1";
const clusterUrl =
process.env.CLUSTER_URL || "https://api.mainnet-beta.solana.com";

View File

@ -5,8 +5,9 @@ import {
PerpMarket,
} from "@blockworks-foundation/mango-client";
import { Market } from "@project-serum/serum";
import { PublicKey } from "@solana/web3.js";
import Big from "big.js";
import { BadRequestError } from "dtos";
import { BadRequestError, RequestErrorCustom } from "dtos";
import { NextFunction, Request, Response, Router } from "express";
import { param, query, validationResult } from "express-validator";
import fetch from "node-fetch";
@ -64,10 +65,18 @@ class MarketsController implements Controller {
response: Response,
next: NextFunction
) => {
response.send({
success: true,
result: await this.fetchMarketsInternal(),
} as MarketsDto);
this.fetchMarketsInternal()
.then((marketsDto) => {
response.send({
success: true,
result: marketsDto,
} as MarketsDto);
})
.catch((error) => {
return response.status(500).send({
errors: [{ msg: error.message } as RequestErrorCustom],
});
});
};
private fetchMarket = async (
@ -83,10 +92,19 @@ class MarketsController implements Controller {
}
const marketName = request.params.market_name;
response.send({
success: true,
result: await this.fetchMarketsInternal(marketName),
} as MarketsDto);
this.fetchMarketsInternal(marketName)
.then((marketsDto) => {
response.send({
success: true,
result: marketsDto,
} as MarketsDto);
})
.catch((error) => {
return response.status(500).send({
errors: [{ msg: error.message } as RequestErrorCustom],
});
});
};
private async fetchMarketsInternal(
@ -230,31 +248,46 @@ class MarketsController implements Controller {
const marketName = request.params.market_name;
const depth = Number(request.query.depth) || 20;
this.getOrderBookInternal(marketName, depth)
.then(({ asks, bids }) => {
return response.send({
success: true,
result: {
asks: asks,
bids: bids,
},
});
})
.catch((error) => {
return response.status(500).send({
errors: [{ msg: error.message } as RequestErrorCustom],
});
});
};
private async getOrderBookInternal(marketName: string, depth: number) {
const ordersInfo = await this.mangoSimpleClient.fetchAllBidsAndAsks(
false,
marketName
);
const bids = ordersInfo
const bids_ = ordersInfo
.flat()
.filter((orderInfo) => orderInfo.order.side === "buy")
.sort((b1, b2) => b2.order.price - b1.order.price);
const asks = ordersInfo
const asks_ = ordersInfo
.flat()
.filter((orderInfo) => orderInfo.order.side === "sell")
.sort((a1, a2) => a1.order.price - a2.order.price);
response.send({
success: true,
result: {
asks: asks
.slice(0, depth)
.map((ask) => [ask.order.price, ask.order.size]),
bids: bids
.slice(0, depth)
.map((bid) => [bid.order.price, bid.order.size]),
},
} as OrdersDto);
};
const asks = asks_
.slice(0, depth)
.map((ask) => [ask.order.price, ask.order.size]);
const bids = bids_
.slice(0, depth)
.map((bid) => [bid.order.price, bid.order.size]);
return { asks, bids };
}
private getTrades = async (
request: Request,
@ -276,28 +309,39 @@ class MarketsController implements Controller {
(marketConfig) => marketConfig.name === marketName
)[0].publicKey;
this.getTradesInternal(marketPk)
.then((tradeDtos) => {
return response.send({
success: true,
result: tradeDtos,
});
})
.catch((error) => {
return response.status(500).send({
errors: [{ msg: error.message } as RequestErrorCustom],
});
});
};
private async getTradesInternal(marketPk: PublicKey) {
const tradesResponse = await fetch(
`https://serum-history.herokuapp.com/trades/address/${marketPk.toBase58()}`
);
const parsedTradesResponse = (await tradesResponse.json()) as any;
let tradeDtos;
if ("s" in parsedTradesResponse && parsedTradesResponse["s"] === "error") {
tradeDtos = [];
} else {
tradeDtos = parsedTradesResponse["data"].map((trade: any) => {
return {
id: trade["orderId"],
liquidation: undefined,
price: trade["price"],
side: trade["side"],
size: trade["size"],
time: new Date(trade["time"]),
} as TradeDto;
});
return [];
}
response.send({ success: true, result: tradeDtos } as TradesDto);
};
return parsedTradesResponse["data"].map((trade: any) => {
return {
id: trade["orderId"],
liquidation: undefined,
price: trade["price"],
side: trade["side"],
size: trade["size"],
time: new Date(trade["time"]),
} as TradeDto;
});
}
private getCandles = async (
request: Request,
@ -316,26 +360,29 @@ class MarketsController implements Controller {
const fromEpochS = Number(request.query.start_time);
const toEpochS = Number(request.query.end_time);
const { t, o, h, l, c, v } = await getOhlcv(
marketName,
resolution,
fromEpochS,
toEpochS
);
const ohlcvDtos: OhlcvDto[] = [];
for (let i = 0; i < t.length; i++) {
ohlcvDtos.push({
time: t[i],
open: o[i],
high: h[i],
low: l[i],
close: c[i],
volume: v[i],
} as OhlcvDto);
}
response.send({ success: true, result: ohlcvDtos } as OhlcvsDto);
await getOhlcv(marketName, resolution, fromEpochS, toEpochS)
.then(({ t, o, h, l, c, v }) => {
const ohlcvDtos: OhlcvDto[] = [];
for (let i = 0; i < t.length; i++) {
ohlcvDtos.push({
time: t[i],
open: o[i],
high: h[i],
low: l[i],
close: c[i],
volume: v[i],
} as OhlcvDto);
}
return response.send({
success: true,
result: ohlcvDtos,
});
})
.catch((error) => {
return response.status(500).send({
errors: [{ msg: error.message } as RequestErrorCustom],
});
});
};
}

View File

@ -1,6 +1,6 @@
import { PerpOrder } from "@blockworks-foundation/mango-client";
import { Order } from "@project-serum/serum/lib/market";
import { BadRequestError, BadRequestErrorCustom } from "dtos";
import { BadRequestError, RequestErrorCustom } from "dtos";
import { NextFunction, Request, Response, Router } from "express";
import { body, query, validationResult } from "express-validator";
import Controller from "./controller.interface";
@ -67,9 +67,144 @@ class OrdersController implements Controller {
.json({ errors: errors.array() as BadRequestError[] });
}
const marketName = request.query.market
? String(request.query.market)
: undefined;
this.getOpenOrdersInternal(marketName)
.then((orderDtos) => {
return response.send({ success: true, result: orderDtos } as OrdersDto);
})
.catch((error) => {
return response.status(500).send({
errors: [{ msg: error.message } as RequestErrorCustom],
});
});
};
private placeOrder = async (
request: Request,
response: Response,
next: NextFunction
) => {
const errors = validationResult(request);
if (!errors.isEmpty()) {
return response
.status(400)
.json({ errors: errors.array() as BadRequestError[] });
}
const placeOrderDto = request.body as PlaceOrderDto;
logger.info(`placing order`);
this.mangoSimpleClient
.placeOrder(
placeOrderDto.market,
placeOrderDto.type,
placeOrderDto.side,
placeOrderDto.size,
placeOrderDto.price,
placeOrderDto.ioc
? "ioc"
: placeOrderDto.postOnly
? "postOnly"
: "limit",
placeOrderDto.clientId
)
.then(() => {
return response.status(200).send();
})
.catch((error) => {
return response.status(500).send({
errors: [{ msg: error.message } as RequestErrorCustom],
});
});
};
private cancelAllOrders = async (
request: Request,
response: Response,
next: NextFunction
) => {
logger.info(`cancelling all orders...`);
this.mangoSimpleClient
.cancelAllOrders()
.then(() => {
return response.status(200).send();
})
.catch((error) => {
return response.status(500).send({
errors: [{ msg: error.message } as RequestErrorCustom],
});
});
};
private cancelOrderByOrderId = async (
request: Request,
response: Response,
next: NextFunction
) => {
const orderId = request.params.order_id;
logger.info(`cancelling order with orderId ${orderId}...`);
this.mangoSimpleClient
.getOrderByOrderId(orderId)
.then((orderInfos) => {
if (!orderInfos.length) {
return response
.status(400)
.json({ errors: [{ msg: "Order not found!" }] });
}
this.mangoSimpleClient
.cancelOrder(orderInfos[0])
.then(() => response.send())
.catch(() => {
return response
.status(500)
.json({ errors: [{ msg: "Unexpected error occured!" }] });
});
})
.catch(() => {
return response
.status(500)
.json({ errors: [{ msg: "Unexpected error occured!" }] });
});
};
private cancelOrderByClientId = async (
request: Request,
response: Response,
next: NextFunction
) => {
const clientId = request.params.client_id;
logger.info(`cancelling order with clientId ${clientId}...`);
this.mangoSimpleClient
.getOrderByClientId(clientId)
.then((orderInfos) => {
if (!orderInfos.length) {
return response
.status(400)
.json({ errors: [{ msg: "Order not found!" }] });
}
this.mangoSimpleClient
.cancelOrder(orderInfos[0])
.then(() => response.send())
.catch(() => {
return response
.status(500)
.json({ errors: [{ msg: "Unexpected error occured!" }] });
});
})
.catch(() => {
return response
.status(500)
.json({ errors: [{ msg: "Unexpected error occured!" }] });
});
};
private async getOpenOrdersInternal(marketName: string) {
const openOrders = await this.mangoSimpleClient.fetchAllBidsAndAsks(
true,
request.query.market ? String(request.query.market) : undefined
marketName
);
const orderDtos = openOrders.flat().map((orderInfo: OrderInfo) => {
@ -121,140 +256,8 @@ class OrdersController implements Controller {
: undefined,
} as OrderDto;
});
response.send({ success: true, result: orderDtos } as OrdersDto);
};
private placeOrder = async (
request: Request,
response: Response,
next: NextFunction
) => {
const errors = validationResult(request);
if (!errors.isEmpty()) {
return response
.status(400)
.json({ errors: errors.array() as BadRequestError[] });
}
const placeOrderDto = request.body as PlaceOrderDto;
logger.info(`placing order`);
try {
await this.mangoSimpleClient.placeOrder(
placeOrderDto.market,
placeOrderDto.type,
placeOrderDto.side,
placeOrderDto.size,
placeOrderDto.price,
placeOrderDto.ioc
? "ioc"
: placeOrderDto.postOnly
? "postOnly"
: "limit",
placeOrderDto.clientId
);
} catch (error) {
return response.status(400).send({
errors: [{ msg: error.message } as BadRequestErrorCustom],
});
}
response.send({
success: true,
result: {
createdAt: new Date(),
filledSize: undefined,
future: placeOrderDto.market,
id: undefined,
market: placeOrderDto.market,
price: undefined,
remainingSize: undefined,
side: placeOrderDto.side,
size: placeOrderDto.size,
status: undefined,
type: placeOrderDto.type,
reduceOnly: undefined,
ioc: placeOrderDto.ioc,
postOnly: placeOrderDto.postOnly,
clientId: placeOrderDto.clientId
? placeOrderDto.clientId.toString()
: null,
},
} as PlaceOrderResponseDto);
};
private cancelAllOrders = async (
request: Request,
response: Response,
next: NextFunction
) => {
logger.info(`cancelling all orders...`);
// todo: leads to 429 if too many orders exist, needs optimization
await this.mangoSimpleClient.cancelAllOrders();
response.send();
};
private cancelOrderByOrderId = async (
request: Request,
response: Response,
next: NextFunction
) => {
const orderId = request.params.order_id;
logger.info(`cancelling order with orderId ${orderId}...`);
this.mangoSimpleClient
.getOrderByOrderId(orderId)
.then((orderInfos) => {
if (!orderInfos.length) {
return response
.status(400)
.json({ errors: [{ msg: "Order not found!" }] });
}
this.mangoSimpleClient
.cancelOrder(orderInfos[0])
.then(() => response.send())
.catch(() => {
return response
.status(400)
.json({ errors: [{ msg: "Unexpected error occured!" }] });
});
})
.catch(() => {
return response
.status(400)
.json({ errors: [{ msg: "Unexpected error occured!" }] });
});
};
private cancelOrderByClientId = async (
request: Request,
response: Response,
next: NextFunction
) => {
const clientId = request.params.client_id;
logger.info(`cancelling order with clientId ${clientId}...`);
this.mangoSimpleClient
.getOrderByClientId(clientId)
.then((orderInfos) => {
if (!orderInfos.length) {
return response
.status(400)
.json({ errors: [{ msg: "Order not found!" }] });
}
this.mangoSimpleClient
.cancelOrder(orderInfos[0])
.then(() => response.send())
.catch(() => {
return response
.status(400)
.json({ errors: [{ msg: "Unexpected error occured!" }] });
});
})
.catch(() => {
return response
.status(400)
.json({ errors: [{ msg: "Unexpected error occured!" }] });
});
};
return orderDtos;
}
}
export default OrdersController;

View File

@ -7,7 +7,7 @@ import {
} from "@blockworks-foundation/mango-client";
import { OpenOrders } from "@project-serum/serum";
import Controller from "controller.interface";
import { BadRequestErrorCustom } from "dtos";
import { RequestErrorCustom } from "dtos";
import e, { NextFunction, Request, Response, Router } from "express";
import { body } from "express-validator";
import { sumBy } from "lodash";
@ -41,6 +41,21 @@ class WalletController implements Controller {
response: Response,
next: NextFunction
) => {
this.fetchBalancesInternal()
.then((balanceDtos) => {
return response.send({
success: true,
result: balanceDtos,
} as BalancesDto);
})
.catch((error) => {
return response.status(500).send({
errors: [{ msg: error.message } as RequestErrorCustom],
});
});
};
private async fetchBalancesInternal() {
// local copies of mango objects
const mangoGroupConfig = this.mangoSimpleClient.mangoGroupConfig;
const mangoGroup = this.mangoSimpleClient.mangoGroup;
@ -67,7 +82,7 @@ class WalletController implements Controller {
name,
} of mangoGroupConfig.spotMarkets) {
if (!mangoAccount || !mangoGroup) {
response.send([]);
return [];
}
const openOrders: OpenOrders =
@ -198,7 +213,6 @@ class WalletController implements Controller {
const value = net.mul(mangoGroup.getPrice(tokenIndex, mangoCache));
/* tslint:enable */
////// end of copy pasta block from mango-ui-v3
// append balances for base symbols
const balanceDtos = baseBalances.map((baseBalance) => {
return {
@ -222,9 +236,8 @@ class WalletController implements Controller {
usdValue: value.toNumber(),
availableWithoutBorrow: net.sub(quoteMeta.borrows).toNumber(),
});
response.send({ success: true, result: balanceDtos } as BalancesDto);
};
return balanceDtos;
}
private withdraw = async (
request: Request,
@ -238,8 +251,8 @@ class WalletController implements Controller {
response.status(200);
})
.catch((error) => {
return response.status(400).send({
errors: [{ msg: error.message } as BadRequestErrorCustom],
return response.status(500).send({
errors: [{ msg: error.message } as RequestErrorCustom],
});
});
};