diff --git a/Cargo.lock b/Cargo.lock index 2699a43..e4c1f43 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1446,7 +1446,7 @@ dependencies = [ [[package]] name = "drift-sdk" version = "0.1.0" -source = "git+https://github.com/drift-labs/drift-rs?rev=8384f6#8384f693a33f0ab98658bf7edad90f795b27bef5" +source = "git+https://github.com/drift-labs/drift-rs?rev=6749bcd#6749bcd2997d15d5d16e70b97b457914dc0d8b72" dependencies = [ "anchor-lang", "base64 0.13.1", diff --git a/Cargo.toml b/Cargo.toml index b7e2694..b137d63 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] actix-web = "4.4.0" argh = "0.1.12" -drift-sdk = { git = "https://github.com/drift-labs/drift-rs", rev = "8384f6" } +drift-sdk = { git = "https://github.com/drift-labs/drift-rs", rev = "6749bcd" } env_logger = "0.10.1" futures-util = "0.3.29" log = "0.4.20" diff --git a/README.md b/README.md index 0c4e1b6..0ce38bc 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ Self hosted API gateway to easily interact with Drift V2 Protocol - [`GET` SOL Balance](#get-sol-balance) - [`GET` Margin Info](#get-margin-info) - [`GET` Leverage](#get-leverage) + - [`GET` Collateral](#get-collateral) - [`POST` Place Orders](#place-orders) - [`PATCH` Modify Orders](#modify-orders) - [`DELETE` Cancel Orders](#cancel-orders) @@ -225,6 +226,22 @@ $ curl localhost:8080/v2/leverage } ``` +## Get Collateral +Returns the account's maintenance collateral + +```bash +$ curl localhost:8080/v2/collateral +``` + +**Response** + +```json +{ + "total":"1661.195815", + "free":"1653.531255" +} +``` + ## Get Market Info Returns market details (perps only) diff --git a/src/controller.rs b/src/controller.rs index 34aa433..91f2053 100644 --- a/src/controller.rs +++ b/src/controller.rs @@ -4,7 +4,8 @@ use drift_sdk::{ dlob_client::DLOBClient, event_subscriber::{try_parse_log, CommitmentConfig}, math::liquidation::{ - calculate_liquidation_price_and_unrealized_pnl, calculate_margin_requirements, + calculate_collateral, calculate_liquidation_price_and_unrealized_pnl, + calculate_margin_requirements, MarginCategory, }, types::{ self, MarketId, MarketType, ModifyOrderParams, RpcSendTransactionConfig, SdkError, @@ -21,13 +22,14 @@ use solana_transaction_status::{option_serializer::OptionSerializer, UiTransacti use std::{borrow::Cow, str::FromStr, sync::Arc}; use thiserror::Error; +use crate::types::UserCollateralResponse; use crate::{ types::{ get_market_decimals, AllMarketsResponse, CancelAndPlaceRequest, CancelOrdersRequest, GetOrderbookRequest, GetOrdersRequest, GetOrdersResponse, GetPositionsRequest, GetPositionsResponse, Market, MarketInfoResponse, ModifyOrdersRequest, Order, OrderbookL2, PerpPosition, PerpPositionExtended, PlaceOrdersRequest, SolBalanceResponse, SpotPosition, - TxEventsResponse, TxResponse, UserMarginResponse, UserLeverageResponse, PRICE_DECIMALS, + TxEventsResponse, TxResponse, UserLeverageResponse, UserMarginResponse, PRICE_DECIMALS, }, websocket::map_drift_event_for_account, Context, LOG_TARGET, @@ -227,6 +229,21 @@ impl AppState { .map_err(|err| ControllerError::Sdk(err)) } + pub async fn get_collateral( + &self, + ctx: Context, + margin_category: Option, + ) -> GatewayResult { + let sub_account = self.resolve_sub_account(ctx.sub_account_id); + calculate_collateral( + &self.client, + &self.client.get_user_account(&sub_account).await?, + margin_category.unwrap_or(MarginCategory::Maintenance), + ) + .map(Into::into) + .map_err(|err| ControllerError::Sdk(err)) + } + pub async fn get_position_extended( &self, ctx: Context, diff --git a/src/main.rs b/src/main.rs index be0f70d..e47c438 100644 --- a/src/main.rs +++ b/src/main.rs @@ -197,6 +197,14 @@ async fn get_leverage(controller: web::Data, ctx: web::Query) handle_result(controller.get_leverage(ctx.0).await) } +#[get("/collateral")] +async fn get_collateral( + controller: web::Data, + ctx: web::Query, +) -> impl Responder { + handle_result(controller.get_collateral(ctx.0, None).await) +} + #[actix_web::main] async fn main() -> std::io::Result<()> { let config: GatewayConfig = argh::from_env(); @@ -283,7 +291,8 @@ async fn main() -> std::io::Result<()> { .service(get_tx_events) .service(get_market_info) .service(get_margin_info) - .service(get_leverage), + .service(get_leverage) + .service(get_collateral), ) }) .keep_alive(Duration::from_secs(config.keep_alive_timeout as u64)) diff --git a/src/types.rs b/src/types.rs index aa8f89e..8c14c31 100644 --- a/src/types.rs +++ b/src/types.rs @@ -2,6 +2,8 @@ //! - gateway request/responses //! - wrappers for presenting drift program types with less implementation detail //! +use drift_sdk::constants::QUOTE_PRECISION; +use drift_sdk::math::liquidation::CollateralInfo; use drift_sdk::{ constants::{ProgramData, BASE_PRECISION, PRICE_PRECISION}, dlob_client::{L2Level, L2Orderbook}, @@ -18,6 +20,7 @@ use crate::websocket::AccountEvent; /// decimal places in price values pub const PRICE_DECIMALS: u32 = PRICE_PRECISION.ilog10(); +pub const QUOTE_DECIMALS: u32 = QUOTE_PRECISION.ilog10(); #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] @@ -629,6 +632,21 @@ impl From for UserLeverageResponse { } } +#[derive(Serialize, Debug)] +pub struct UserCollateralResponse { + pub total: Decimal, + pub free: Decimal, +} + +impl From for UserCollateralResponse { + fn from(value: CollateralInfo) -> Self { + Self { + total: Decimal::from_i128_with_scale(value.total, QUOTE_DECIMALS).normalize(), + free: Decimal::from_i128_with_scale(value.free, QUOTE_DECIMALS).normalize(), + } + } +} + #[cfg(test)] mod tests { use drift_sdk::{