withdraw
Signed-off-by: microwavedcola1 <microwavedcola@gmail.com>
This commit is contained in:
parent
2204b8c758
commit
17a70b4155
|
@ -33,7 +33,6 @@ losely sorted in order of importance/priority
|
|||
- stop loss,
|
||||
- market orders
|
||||
- modify order
|
||||
- funding rates
|
||||
- withdraw
|
||||
- funding payments
|
||||
- advanced order types e.g. split
|
||||
|
|
|
@ -277,6 +277,95 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "wallet - withdraw",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"coin\": \"USDC\",\n \"size\": 1000\n}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/wallet/withdrawals",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"wallet",
|
||||
"withdrawals"
|
||||
]
|
||||
}
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
"name": "wallet - withdraw",
|
||||
"originalRequest": {
|
||||
"method": "POST",
|
||||
"header": [],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"coin\": \"USDC\",\n \"size\": 1000\n}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/wallet/withdrawals",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"wallet",
|
||||
"withdrawals"
|
||||
]
|
||||
}
|
||||
},
|
||||
"status": "Bad Request",
|
||||
"code": 400,
|
||||
"_postman_previewlanguage": "json",
|
||||
"header": [
|
||||
{
|
||||
"key": "X-Powered-By",
|
||||
"value": "Express"
|
||||
},
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json; charset=utf-8"
|
||||
},
|
||||
{
|
||||
"key": "Content-Length",
|
||||
"value": "98"
|
||||
},
|
||||
{
|
||||
"key": "ETag",
|
||||
"value": "W/\"62-a5tgkKEcwWc3BLd/jTRTDN5QwDI\""
|
||||
},
|
||||
{
|
||||
"key": "Date",
|
||||
"value": "Fri, 24 Sep 2021 14:00:15 GMT"
|
||||
},
|
||||
{
|
||||
"key": "Connection",
|
||||
"value": "keep-alive"
|
||||
},
|
||||
{
|
||||
"key": "Keep-Alive",
|
||||
"value": "timeout=5"
|
||||
}
|
||||
],
|
||||
"cookie": [],
|
||||
"body": "{\n \"errors\": [\n {\n \"msg\": \"Transaction failed: MangoErrorCode::InsufficientFunds; src/processor.rs:831\"\n }\n ]\n}"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "markets - get all",
|
||||
"request": {
|
||||
|
@ -875,7 +964,7 @@
|
|||
"header": [],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"market\": \"BTC-PERP\",\n \"side\": \"buy\",\n \"price\": 20000,\n \"type\": \"limit\",\n \"size\": 0.0001,\n \"reduceOnly\": false,\n \"ioc\": false,\n \"postOnly\": false,\n \"clientId\": 123\n}\n",
|
||||
"raw": "{\n \"market\": \"BTC-PERP\",\n \"side\": \"buy\",\n \"price\": 20000,\n \"type\": \"limit\",\n \"size\": 0.0001,\n \"reduceOnly\": false,\n \"ioc\": false,\n \"postOnly\": false,\n \"clientId\": \"{{$randomInt}}\"\n}\n",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
|
|
|
@ -205,6 +205,60 @@ paths:
|
|||
total: 50.000004999999994
|
||||
usdValue: 50.000004999999994
|
||||
availableWithoutBorrow: 50.000004999999994
|
||||
/wallet/withdrawals:
|
||||
post:
|
||||
tags:
|
||||
- default
|
||||
summary: wallet - withdraw
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
example:
|
||||
coin: USDC
|
||||
size: 1000
|
||||
responses:
|
||||
'400':
|
||||
description: Bad Request
|
||||
headers:
|
||||
X-Powered-By:
|
||||
schema:
|
||||
type: string
|
||||
example: Express
|
||||
Content-Type:
|
||||
schema:
|
||||
type: string
|
||||
example: application/json; charset=utf-8
|
||||
Content-Length:
|
||||
schema:
|
||||
type: integer
|
||||
example: '98'
|
||||
ETag:
|
||||
schema:
|
||||
type: string
|
||||
example: W/"62-a5tgkKEcwWc3BLd/jTRTDN5QwDI"
|
||||
Date:
|
||||
schema:
|
||||
type: string
|
||||
example: Fri, 24 Sep 2021 14:00:15 GMT
|
||||
Connection:
|
||||
schema:
|
||||
type: string
|
||||
example: keep-alive
|
||||
Keep-Alive:
|
||||
schema:
|
||||
type: string
|
||||
example: timeout=5
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
example:
|
||||
errors:
|
||||
- msg: >-
|
||||
Transaction failed: MangoErrorCode::InsufficientFunds;
|
||||
src/processor.rs:831
|
||||
/markets:
|
||||
get:
|
||||
tags:
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
getMarketByBaseSymbolAndKind,
|
||||
getMarketByPublicKey,
|
||||
getMultipleAccounts,
|
||||
getTokenBySymbol,
|
||||
GroupConfig,
|
||||
MangoAccount,
|
||||
MangoClient,
|
||||
|
@ -23,12 +24,14 @@ import {
|
|||
Commitment,
|
||||
Connection,
|
||||
PublicKey,
|
||||
TransactionSignature,
|
||||
} from "@solana/web3.js";
|
||||
import fs from "fs";
|
||||
import fetch from "node-fetch";
|
||||
import os from "os";
|
||||
import { OrderInfo } from "types";
|
||||
import { logger, zipDict } from "./utils";
|
||||
import BN from "bn.js";
|
||||
|
||||
class MangoSimpleClient {
|
||||
constructor(
|
||||
|
@ -42,25 +45,6 @@ class MangoSimpleClient {
|
|||
setInterval(this.roundRobinClusterUrl, 20_000);
|
||||
}
|
||||
|
||||
private roundRobinClusterUrl() {
|
||||
if (process.env.CLUSTER_URL) {
|
||||
return;
|
||||
}
|
||||
|
||||
let possibleClustersUrls = [
|
||||
"https://api.mainnet-beta.solana.com",
|
||||
"https://lokidfxnwlabdq.main.genesysgo.net:8899/",
|
||||
"https://solana-api.projectserum.com/",
|
||||
];
|
||||
const clusterUrl =
|
||||
possibleClustersUrls[
|
||||
Math.floor(Math.random() * possibleClustersUrls.length)
|
||||
];
|
||||
|
||||
logger.info(`switching to rpc node - ${clusterUrl}...`);
|
||||
this.connection = new Connection(clusterUrl, "processed" as Commitment);
|
||||
}
|
||||
|
||||
static async create() {
|
||||
const groupName = "mainnet.1";
|
||||
const clusterUrl =
|
||||
|
@ -429,7 +413,8 @@ class MangoSimpleClient {
|
|||
side,
|
||||
price,
|
||||
quantity,
|
||||
orderType
|
||||
orderType,
|
||||
new BN(clientOrderId)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -510,6 +495,28 @@ class MangoSimpleClient {
|
|||
return orderInfos;
|
||||
}
|
||||
|
||||
public async withdraw(
|
||||
tokenSymbol: string,
|
||||
amount: number
|
||||
): Promise<TransactionSignature> {
|
||||
const tokenToWithdraw = getTokenBySymbol(
|
||||
this.mangoGroupConfig,
|
||||
tokenSymbol
|
||||
);
|
||||
const tokenIndex = this.mangoGroup.getTokenIndex(tokenToWithdraw.mintKey);
|
||||
return this.client.withdraw(
|
||||
this.mangoGroup,
|
||||
this.mangoAccount,
|
||||
this.owner,
|
||||
this.mangoGroup.tokens[tokenIndex].rootBank,
|
||||
this.mangoGroup.rootBankAccounts[tokenIndex].nodeBankAccounts[0]
|
||||
.publicKey,
|
||||
this.mangoGroup.rootBankAccounts[tokenIndex].nodeBankAccounts[0].vault,
|
||||
Number(amount),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/// private
|
||||
|
||||
private parseSpotOrders(
|
||||
|
@ -572,6 +579,25 @@ class MangoSimpleClient {
|
|||
market: { account: market, config },
|
||||
}));
|
||||
}
|
||||
|
||||
private roundRobinClusterUrl() {
|
||||
if (process.env.CLUSTER_URL) {
|
||||
return;
|
||||
}
|
||||
|
||||
let possibleClustersUrls = [
|
||||
"https://api.mainnet-beta.solana.com",
|
||||
"https://lokidfxnwlabdq.main.genesysgo.net:8899/",
|
||||
"https://solana-api.projectserum.com/",
|
||||
];
|
||||
const clusterUrl =
|
||||
possibleClustersUrls[
|
||||
Math.floor(Math.random() * possibleClustersUrls.length)
|
||||
];
|
||||
|
||||
logger.info(`switching to rpc node - ${clusterUrl}...`);
|
||||
this.connection = new Connection(clusterUrl, "processed" as Commitment);
|
||||
}
|
||||
}
|
||||
|
||||
export default MangoSimpleClient;
|
||||
|
|
|
@ -22,6 +22,10 @@ const allMarketNames = mangoGroupConfig.spotMarkets
|
|||
)
|
||||
);
|
||||
|
||||
const allCoins = mangoGroupConfig.tokens.map(
|
||||
(tokenConfig) => tokenConfig.symbol
|
||||
);
|
||||
|
||||
/// general
|
||||
|
||||
export function zipDict<K extends string | number | symbol, V>(
|
||||
|
@ -47,3 +51,10 @@ export const isValidMarket: CustomValidator = (marketName) => {
|
|||
}
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
export const isValidCoin: CustomValidator = (coin) => {
|
||||
if (allCoins.indexOf(coin) === -1) {
|
||||
return Promise.reject(`Coin ${coin} not supported!`);
|
||||
}
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
|
|
@ -7,11 +7,13 @@ import {
|
|||
} from "@blockworks-foundation/mango-client";
|
||||
import { OpenOrders } from "@project-serum/serum";
|
||||
import Controller from "controller.interface";
|
||||
import { NextFunction, Request, Response, Router } from "express";
|
||||
import { BadRequestErrorCustom } from "dtos";
|
||||
import e, { NextFunction, Request, Response, Router } from "express";
|
||||
import { body } from "express-validator";
|
||||
import { sumBy } from "lodash";
|
||||
import MangoSimpleClient from "mango.simple.client";
|
||||
import { Balances } from "./types";
|
||||
import { i80f48ToPercent } from "./utils";
|
||||
import { i80f48ToPercent, isValidCoin } from "./utils";
|
||||
|
||||
class WalletController implements Controller {
|
||||
public path = "/api/wallet";
|
||||
|
@ -22,7 +24,16 @@ class WalletController implements Controller {
|
|||
}
|
||||
|
||||
private initializeRoutes() {
|
||||
// POST /wallet/balances
|
||||
this.router.get(`${this.path}/balances`, this.fetchBalances);
|
||||
|
||||
// POST /wallet/withdrawals
|
||||
this.router.post(
|
||||
`${this.path}/withdrawals`,
|
||||
body("coin").not().isEmpty().custom(isValidCoin),
|
||||
body("size").isNumeric(),
|
||||
this.withdraw
|
||||
);
|
||||
}
|
||||
|
||||
private fetchBalances = async (
|
||||
|
@ -214,6 +225,24 @@ class WalletController implements Controller {
|
|||
|
||||
response.send({ success: true, result: balanceDtos } as BalancesDto);
|
||||
};
|
||||
|
||||
private withdraw = async (
|
||||
request: Request,
|
||||
response: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
const withdrawDto = request.body as WithdrawDto;
|
||||
this.mangoSimpleClient
|
||||
.withdraw(withdrawDto.coin, withdrawDto.size)
|
||||
.then(() => {
|
||||
response.status(200);
|
||||
})
|
||||
.catch((error) => {
|
||||
return response.status(400).send({
|
||||
errors: [{ msg: error.message } as BadRequestErrorCustom],
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export default WalletController;
|
||||
|
@ -248,3 +277,23 @@ interface BalanceDto {
|
|||
usdValue: number;
|
||||
availableWithoutBorrow: number;
|
||||
}
|
||||
|
||||
// e.g.
|
||||
// {
|
||||
// "coin": "USDTBEAR",
|
||||
// "size": 20.2,
|
||||
// "address": "0x83a127952d266A6eA306c40Ac62A4a70668FE3BE",
|
||||
// "tag": null,
|
||||
// "password": "my_withdrawal_password",
|
||||
// "code": 152823
|
||||
// }
|
||||
|
||||
interface WithdrawDto {
|
||||
coin: string;
|
||||
size: number;
|
||||
// unused
|
||||
address: undefined;
|
||||
tag: undefined;
|
||||
password: undefined;
|
||||
code: undefined;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue