Skip to content

Commit

Permalink
Fix spot position balance
Browse files Browse the repository at this point in the history
  • Loading branch information
jordy25519 committed Dec 14, 2023
1 parent fe09ae5 commit 6bdaec6
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 65 deletions.
29 changes: 14 additions & 15 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

52 changes: 39 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,22 @@ Options:

### Get Market Info
```bash
~: curl localhost:8080/v2/markets
$> curl localhost:8080/v2/markets
```

### Get Orders
```bash
~: curl localhost:8080/v2/orders
$> curl localhost:8080/v2/orders
```

### Get Positions
```bash
~: curl localhost:8080/v2/positions
$> curl localhost:8080/v2/positions
```

### Place Orders
```bash
~: curl localhost:8080/v2/orders -X POST -H 'content-type: application/json' -d '{
$> curl localhost:8080/v2/orders -X POST -H 'content-type: application/json' -d '{
"orders": [{
"marketId": 1,
"marketType": "spot",
Expand All @@ -71,7 +71,7 @@ Options:
### Modify Orders
like place orders but specify either `orderId` or `userOrderId` to indicate which order to modify
```bash
~: curl localhost:8080/v2/orders -X PATCH -H 'content-type: application/json' -d '{
$> curl localhost:8080/v2/orders -X PATCH -H 'content-type: application/json' -d '{
"orders": [{
"marketId": 1,
"marketType": "spot",
Expand All @@ -96,21 +96,47 @@ like place orders but specify either `orderId` or `userOrderId` to indicate whic
### Cancelling Orders
```bash
# cancel by market id
~: curl localhost:8080/v2/orders -X DELETE -H 'content-type: application/json' -d '{"market":{"id":1,"type":"perp"}}'
$> curl localhost:8080/v2/orders -X DELETE -H 'content-type: application/json' -d '{"market":{"id":1,"type":"perp"}}'
# cancel by order ids
~: curl localhost:8080/v2/orders -X DELETE -H 'content-type: application/json' -d '{"ids":[1,2,3,4]}'
$> curl localhost:8080/v2/orders -X DELETE -H 'content-type: application/json' -d '{"ids":[1,2,3,4]}'
# cancel by user assigned order ids
~: curl localhost:8080/v2/orders -X DELETE -H 'content-type: application/json' -d '{"userIds":[1,2,3,4]}'
$> curl localhost:8080/v2/orders -X DELETE -H 'content-type: application/json' -d '{"userIds":[1,2,3,4]}'
# cancel all
~: curl localhost:8080/v2/orders -X DELETE -H 'content-type: application/json'
$> curl localhost:8080/v2/orders -X DELETE -H 'content-type: application/json'
```

### Stream Orderbook
```bash
~: curl localhost:8080/v2/orderbooks -N -X GET -H 'content-type: application/json' -d '{"market":{"id":3,"type":"perp"}}'
$> curl localhost:8080/v2/orderbooks -N -X GET -H 'content-type: application/json' -d '{"market":{"id":3,"type":"perp"}'
```


# TODO:
- return error status for failed txs?
- allow empty json body on queries
- implement orderbook ws stream
- parse/return error codes for failed txs
- integration tests for the endpoints
```rs
Sdk(
Rpc(
ClientError {
request: Some(SendTransaction),
kind: RpcError(
RpcResponseError { code: -32002, message: "Transaction simulation failed: Error processing Instruction 0: custom program error: 0x17b7", data: SendTransactionPreflightFailure(
RpcSimulateTransactionResult {
err: Some(InstructionError(0, Custom(6071))),
logs: Some([
"Program dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH invoke [1]",
"Program log: Instruction: PlaceOrders",
"Program log: user_order_id is already in use 101",
"Program log: AnchorError occurred. Error Code: UserOrderIdAlreadyInUse. Error Number: 6071. Error Message: User Order Id Already In Use.",
"Program dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH consumed 15857 of 200000 compute units",
"Program dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH failed: custom program error: 0x17b7"
]),
accounts: None,
units_consumed: Some(0),
return_data: None
})
})
}
)
)
```
53 changes: 32 additions & 21 deletions src/controller.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
use std::task::Poll;
use std::{sync::Arc, task::Poll};

use actix_web::{web::Bytes, Error};
use drift_sdk::{
dlob::{DLOBClient, OrderbookStream},
types::{Context, MarketType, OrderParams, SdkError},
DriftClient, Pubkey, TransactionBuilder, Wallet,
types::{Context, MarketType, OrderParams, SdkError, SdkResult},
DriftClient, Pubkey, TransactionBuilder, Wallet, WsAccountProvider,
};
use futures_util::{Stream, StreamExt};
use futures_util::{stream::FuturesUnordered, Stream, StreamExt};
use log::error;
use thiserror::Error;

use crate::types::{
AllMarketsResponse, CancelOrdersRequest, GetOrderbookRequest, GetOrdersRequest,
GetOrdersResponse, GetPositionsRequest, GetPositionsResponse, ModifyOrdersRequest, Order,
PlaceOrdersRequest,
PlaceOrdersRequest, SpotPosition,
};

#[derive(Error, Debug)]
Expand All @@ -27,7 +27,7 @@ pub enum ControllerError {
#[derive(Clone)]
pub struct AppState {
wallet: Wallet,
client: DriftClient,
client: Arc<DriftClient<WsAccountProvider>>,
dlob_client: DLOBClient,
}

Expand All @@ -54,11 +54,10 @@ impl AppState {
secret_key,
)
.expect("valid key");
let client = DriftClient::new(endpoint).await.expect("connects");
client
.subscribe_account(wallet.user())
let account_provider = WsAccountProvider::new(endpoint).await.expect("ws connects");
let client = DriftClient::new(endpoint, account_provider)
.await
.expect("cache on");
.expect("ok");

let dlob_endpoint = if devnet {
"https://master.dlob.drift.trade"
Expand All @@ -67,7 +66,7 @@ impl AppState {
};
Self {
wallet,
client,
client: Arc::new(client),
dlob_client: DLOBClient::new(dlob_endpoint),
}
}
Expand All @@ -80,7 +79,7 @@ impl AppState {
/// 3) ids are given, cancel all orders by id (global, exchange assigned id)
/// 4) catch all. cancel all orders
pub async fn cancel_orders(&self, req: CancelOrdersRequest) -> Result<String, ControllerError> {
let user_data = self.client.get_account_data(self.user()).await?;
let user_data = self.client.get_user_account(self.user()).await?;
let builder = TransactionBuilder::new(&self.wallet, &user_data);

let tx = if let Some(market) = req.market {
Expand Down Expand Up @@ -110,9 +109,12 @@ impl AppState {
&self,
req: GetPositionsRequest,
) -> Result<GetPositionsResponse, ControllerError> {
let (spot, perp) = self.client.all_positions(self.user()).await?;
Ok(GetPositionsResponse {
spot: spot
let (all_spot, all_perp) = self.client.all_positions(self.user()).await?;

// calculating spot token balance requires knowing the 'spot market account' data
let mut filtered_spot_positions = Vec::<SpotPosition>::with_capacity(all_spot.len());
let mut filtered_spot_futs = FuturesUnordered::from_iter(
all_spot
.iter()
.filter(|p| {
if let Some(ref market) = req.market {
Expand All @@ -121,15 +123,24 @@ impl AppState {
true
}
})
.map(|x| (*x).into())
.collect(),
perp: perp
.map(|x| async {
let spot_market_info = self.client.get_spot_market_info(x.market_index).await?;
SdkResult::Ok(SpotPosition::from_sdk_type(x, &spot_market_info))
}),
);
while let Some(result) = filtered_spot_futs.next().await {
filtered_spot_positions.push(result?);
}

Ok(GetPositionsResponse {
spot: filtered_spot_positions,
perp: all_perp
.iter()
.filter(|p| {
if let Some(ref market) = req.market {
p.market_index == market.id && MarketType::Perp == market.market_type
} else {
p.base_asset_amount != 0
true
}
})
.map(|x| (*x).into())
Expand Down Expand Up @@ -176,7 +187,7 @@ impl AppState {
.collect();
let tx = TransactionBuilder::new(
&self.wallet,
&self.client.get_account_data(self.user()).await?,
&self.client.get_user_account(self.user()).await?,
)
.place_orders(orders)
.build();
Expand All @@ -187,7 +198,7 @@ impl AppState {
}

pub async fn modify_orders(&self, req: ModifyOrdersRequest) -> Result<String, ControllerError> {
let user_data = &self.client.get_account_data(self.user()).await?;
let user_data = &self.client.get_user_account(self.user()).await?;

let mut params = Vec::<(u32, OrderParams)>::with_capacity(req.orders.len());
for order in req.orders {
Expand Down
33 changes: 27 additions & 6 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,22 @@ async fn get_markets(controller: web::Data<AppState>) -> impl Responder {
#[get("/orders")]
async fn get_orders(
controller: web::Data<AppState>,
req: Json<GetOrdersRequest>,
body: actix_web::web::Bytes,
) -> impl Responder {
match controller.get_orders(req.0).await {
let mut req = GetOrdersRequest::default();
if body.len() > 0 {
match serde_json::from_slice(body.as_ref()) {
Ok(deser) => req = deser,
Err(err) => {
return Either::Left(HttpResponse::BadRequest().message_body(err.to_string()))
}
}
};

match controller.get_orders(req).await {
Err(err) => {
error!("{err:?}");
Either::Left(HttpResponse::InternalServerError())
Either::Left(HttpResponse::InternalServerError().message_body(err.to_string()))
}
Ok(payload) => Either::Right(Json(payload)),
}
Expand All @@ -42,6 +52,7 @@ async fn create_orders(
) -> impl Responder {
match controller.place_orders(req.0).await {
Err(err) => {
// TODO: convert into http status code / return error to client
error!("{err:?}");
Either::Left(HttpResponse::InternalServerError())
}
Expand Down Expand Up @@ -81,12 +92,22 @@ async fn cancel_orders(
#[get("/positions")]
async fn get_positions(
controller: web::Data<AppState>,
req: Json<GetPositionsRequest>,
body: actix_web::web::Bytes,
) -> impl Responder {
match controller.get_positions(req.0).await {
let mut req = GetPositionsRequest::default();
if body.len() > 0 {
match serde_json::from_slice(body.as_ref()) {
Ok(deser) => req = deser,
Err(err) => {
return Either::Left(HttpResponse::BadRequest().message_body(err.to_string()))
}
}
};

match controller.get_positions(req).await {
Err(err) => {
error!("{err:?}");
Either::Left(HttpResponse::InternalServerError())
Either::Left(HttpResponse::InternalServerError().message_body(err.to_string()))
}
Ok(payload) => Either::Right(Json(payload)),
}
Expand Down
Loading

0 comments on commit 6bdaec6

Please sign in to comment.