error handling
Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>
This commit is contained in:
parent
096ddee727
commit
4019b622af
|
@ -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;
|
||||
|
|
|
@ -5,6 +5,6 @@ export interface BadRequestError {
|
|||
location: string;
|
||||
}
|
||||
|
||||
export interface BadRequestErrorCustom {
|
||||
export interface RequestErrorCustom {
|
||||
msg: string;
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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],
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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],
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue