From 0c5ad065ba2cfb213f98b9266eacea565ddecb4f Mon Sep 17 00:00:00 2001 From: jordy25519 Date: Thu, 19 Sep 2024 09:50:53 +0800 Subject: [PATCH] Feat/less rpc (#74) * Subscribe to default sub account via Ws updates * Use Ws Maps for user accounta and oracle price queries --- .rustfmt.toml | 3 + Cargo.lock | 377 ++++------------------------------------------ Cargo.toml | 6 +- src/controller.rs | 196 ++++++++++++++---------- src/main.rs | 9 +- src/types.rs | 12 +- 6 files changed, 163 insertions(+), 440 deletions(-) create mode 100644 .rustfmt.toml diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 0000000..3590a3b --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1,3 @@ +group_imports = "StdExternalCrate" +imports_granularity = "Crate" +use_field_init_shorthand = true \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 1567f03..3898405 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -49,7 +49,7 @@ dependencies = [ "encoding_rs", "flate2", "futures-core", - "h2 0.3.26", + "h2", "http 0.2.12", "httparse", "httpdate", @@ -491,55 +491,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "anstream" -version = "0.6.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" - -[[package]] -name = "anstyle-parse" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" -dependencies = [ - "anstyle", - "windows-sys 0.52.0", -] - [[package]] name = "anyhow" version = "1.0.86" @@ -808,12 +759,6 @@ dependencies = [ "syn 2.0.58", ] -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - [[package]] name = "atty" version = "0.2.14" @@ -1283,12 +1228,6 @@ dependencies = [ "os_str_bytes", ] -[[package]] -name = "colorchoice" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" - [[package]] name = "combine" version = "3.8.1" @@ -1674,8 +1613,8 @@ dependencies = [ [[package]] name = "drift" -version = "2.92.0" -source = "git+https://github.com/drift-labs/protocol-v2.git?tag=v2.92.0#9d630045c170ef382c2355996cec0f9ecdb9fb8f" +version = "2.93.0" +source = "git+https://github.com/drift-labs/protocol-v2.git?tag=v2.93.0#02a0e05ed496025d44a85a4119cdf65d14cf9457" dependencies = [ "ahash 0.8.6", "anchor-lang", @@ -1707,13 +1646,13 @@ dependencies = [ [[package]] name = "drift-gateway" -version = "1.0.1" +version = "1.0.2" dependencies = [ "actix-web", "anchor-lang", "argh", "drift-sdk", - "env_logger 0.11.5", + "env_logger 0.9.3", "futures-util", "log", "rust_decimal", @@ -1724,7 +1663,7 @@ dependencies = [ "solana-transaction-status", "thiserror", "tokio", - "tokio-tungstenite 0.23.1", + "tokio-tungstenite 0.17.2", ] [[package]] @@ -1739,7 +1678,7 @@ dependencies = [ [[package]] name = "drift-sdk" version = "0.1.0" -source = "git+https://github.com/drift-labs/drift-rs?rev=9d90de9#9d90de968359a73567ee722dcac6a236f679a72c" +source = "git+https://github.com/drift-labs/drift-rs?rev=59e3e939#59e3e939831b3100dc47fe3ad19cf4c1802d2124" dependencies = [ "anchor-lang", "base64 0.13.1", @@ -1752,7 +1691,7 @@ dependencies = [ "log", "rayon", "regex", - "reqwest 0.12.4", + "reqwest", "serde", "serde_json", "solana-account-decoder", @@ -1880,16 +1819,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "env_filter" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" -dependencies = [ - "log", - "regex", -] - [[package]] name = "env_logger" version = "0.9.3" @@ -1916,19 +1845,6 @@ dependencies = [ "termcolor", ] -[[package]] -name = "env_logger" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" -dependencies = [ - "anstream", - "anstyle", - "env_filter", - "humantime", - "log", -] - [[package]] name = "equivalent" version = "1.0.1" @@ -2200,25 +2116,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "h2" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http 1.1.0", - "indexmap 2.4.0", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "hash32" version = "0.2.1" @@ -2369,29 +2266,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http 1.1.0", -] - -[[package]] -name = "http-body-util" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" -dependencies = [ - "bytes", - "futures-util", - "http 1.1.0", - "http-body 1.0.1", - "pin-project-lite", -] - [[package]] name = "httparse" version = "1.9.4" @@ -2420,9 +2294,9 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2 0.3.26", + "h2", "http 0.2.12", - "http-body 0.4.6", + "http-body", "httparse", "httpdate", "itoa", @@ -2434,26 +2308,6 @@ dependencies = [ "want", ] -[[package]] -name = "hyper" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "h2 0.4.6", - "http 1.1.0", - "http-body 1.0.1", - "httparse", - "itoa", - "pin-project-lite", - "smallvec", - "tokio", - "want", -] - [[package]] name = "hyper-rustls" version = "0.24.2" @@ -2462,7 +2316,7 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http 0.2.12", - "hyper 0.14.30", + "hyper", "rustls 0.21.12", "tokio", "tokio-rustls 0.24.1", @@ -2470,38 +2324,15 @@ dependencies = [ [[package]] name = "hyper-tls" -version = "0.6.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "http-body-util", - "hyper 1.4.1", - "hyper-util", + "hyper", "native-tls", "tokio", "tokio-native-tls", - "tower-service", -] - -[[package]] -name = "hyper-util" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "http 1.1.0", - "http-body 1.0.1", - "hyper 1.4.1", - "pin-project-lite", - "socket2 0.5.7", - "tokio", - "tower", - "tower-service", - "tracing", ] [[package]] @@ -2630,12 +2461,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "is_terminal_polyfill" -version = "1.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" - [[package]] name = "itertools" version = "0.9.0" @@ -3207,7 +3032,7 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openbook-v2-light" version = "0.1.0" -source = "git+https://github.com/drift-labs/protocol-v2.git?tag=v2.92.0#9d630045c170ef382c2355996cec0f9ecdb9fb8f" +source = "git+https://github.com/drift-labs/protocol-v2.git?tag=v2.93.0#02a0e05ed496025d44a85a4119cdf65d14cf9457" dependencies = [ "anchor-lang", "borsh 0.10.3", @@ -3355,26 +3180,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "pin-project" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" -dependencies = [ - "proc-macro2 1.0.79", - "quote 1.0.36", - "syn 2.0.58", -] - [[package]] name = "pin-project-lite" version = "0.2.14" @@ -3830,26 +3635,29 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2 0.3.26", + "h2", "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.30", + "http-body", + "hyper", "hyper-rustls", + "hyper-tls", "ipnet", "js-sys", "log", "mime", + "native-tls", "once_cell", "percent-encoding", "pin-project-lite", "rustls 0.21.12", - "rustls-pemfile 1.0.4", + "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", "system-configuration", "tokio", + "tokio-native-tls", "tokio-rustls 0.24.1", "tokio-util", "tower-service", @@ -3858,49 +3666,7 @@ dependencies = [ "wasm-bindgen-futures", "web-sys", "webpki-roots 0.25.4", - "winreg 0.50.0", -] - -[[package]] -name = "reqwest" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" -dependencies = [ - "base64 0.22.1", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2 0.4.6", - "http 1.1.0", - "http-body 1.0.1", - "http-body-util", - "hyper 1.4.1", - "hyper-tls", - "hyper-util", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls-pemfile 2.1.3", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "system-configuration", - "tokio", - "tokio-native-tls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg 0.52.0", + "winreg", ] [[package]] @@ -4073,7 +3839,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ "openssl-probe", - "rustls-pemfile 1.0.4", + "rustls-pemfile", "schannel", "security-framework", ] @@ -4087,22 +3853,6 @@ dependencies = [ "base64 0.21.7", ] -[[package]] -name = "rustls-pemfile" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" -dependencies = [ - "base64 0.22.1", - "rustls-pki-types", -] - -[[package]] -name = "rustls-pki-types" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" - [[package]] name = "rustls-webpki" version = "0.101.7" @@ -4718,7 +4468,7 @@ dependencies = [ "gethostname", "lazy_static", "log", - "reqwest 0.11.27", + "reqwest", "solana-sdk", ] @@ -4863,7 +4613,7 @@ dependencies = [ "crossbeam-channel", "futures-util", "log", - "reqwest 0.11.27", + "reqwest", "semver", "serde", "serde_derive", @@ -4948,7 +4698,7 @@ dependencies = [ "bs58 0.4.0", "indicatif", "log", - "reqwest 0.11.27", + "reqwest", "semver", "serde", "serde_derive", @@ -4971,7 +4721,7 @@ dependencies = [ "base64 0.21.7", "bs58 0.4.0", "jsonrpc-core", - "reqwest 0.11.27", + "reqwest", "semver", "serde", "serde_derive", @@ -5531,7 +5281,7 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "switchboard" version = "0.1.0" -source = "git+https://github.com/drift-labs/protocol-v2.git?tag=v2.92.0#9d630045c170ef382c2355996cec0f9ecdb9fb8f" +source = "git+https://github.com/drift-labs/protocol-v2.git?tag=v2.93.0#02a0e05ed496025d44a85a4119cdf65d14cf9457" dependencies = [ "anchor-lang", ] @@ -5539,7 +5289,7 @@ dependencies = [ [[package]] name = "switchboard-on-demand" version = "0.1.0" -source = "git+https://github.com/drift-labs/protocol-v2.git?tag=v2.92.0#9d630045c170ef382c2355996cec0f9ecdb9fb8f" +source = "git+https://github.com/drift-labs/protocol-v2.git?tag=v2.93.0#02a0e05ed496025d44a85a4119cdf65d14cf9457" dependencies = [ "anchor-lang", "bytemuck", @@ -5859,18 +5609,6 @@ dependencies = [ "tungstenite 0.21.0", ] -[[package]] -name = "tokio-tungstenite" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6989540ced10490aaf14e6bad2e3d33728a2813310a0c71d1574304c49631cd" -dependencies = [ - "futures-util", - "log", - "tokio", - "tungstenite 0.23.0", -] - [[package]] name = "tokio-util" version = "0.7.11" @@ -5921,27 +5659,6 @@ dependencies = [ "winnow", ] -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "futures-core", - "futures-util", - "pin-project", - "pin-project-lite", - "tokio", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - [[package]] name = "tower-service" version = "0.3.3" @@ -6028,24 +5745,6 @@ dependencies = [ "utf-8", ] -[[package]] -name = "tungstenite" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e2ce1e47ed2994fd43b04c8f618008d4cabdd5ee34027cf14f9d918edd9c8" -dependencies = [ - "byteorder", - "bytes", - "data-encoding", - "http 1.1.0", - "httparse", - "log", - "rand 0.8.5", - "sha1", - "thiserror", - "utf-8", -] - [[package]] name = "typenum" version = "1.17.0" @@ -6176,12 +5875,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - [[package]] name = "uuid" version = "1.10.0" @@ -6599,16 +6292,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "winreg" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "without-alloc" version = "0.2.2" diff --git a/Cargo.toml b/Cargo.toml index 953175d..d3de044 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "drift-gateway" -version = "1.0.1" +version = "1.0.2" edition = "2021" [dependencies] @@ -8,7 +8,7 @@ actix-web = "*" # pinned to match drift program version anchor-lang = "=0.29.0" argh = "*" -drift-sdk = { git = "https://github.com/drift-labs/drift-rs", rev = "9d90de9" } +drift-sdk = { git = "https://github.com/drift-labs/drift-rs", rev = "59e3e939" } env_logger = "*" futures-util = "*" log = "*" @@ -20,4 +20,4 @@ solana-sdk = "1.16" solana-transaction-status = "1.16" thiserror = "*" tokio = "*" -tokio-tungstenite = "*" \ No newline at end of file +tokio-tungstenite = "*" diff --git a/src/controller.rs b/src/controller.rs index b6d259c..8df83a1 100644 --- a/src/controller.rs +++ b/src/controller.rs @@ -1,34 +1,35 @@ -use drift_sdk::math::leverage::get_leverage; +use std::{borrow::Cow, str::FromStr, sync::Arc}; + use drift_sdk::{ constants::{ProgramData, BASE_PRECISION}, event_subscriber::{try_parse_log, CommitmentConfig}, - math::liquidation::{ - calculate_collateral, calculate_liquidation_price_and_unrealized_pnl, - calculate_margin_requirements, MarginCategory, + math::{ + leverage::get_leverage, + liquidation::{ + calculate_collateral, calculate_liquidation_price_and_unrealized_pnl, + calculate_margin_requirements, MarginCategory, + }, }, types::{ self, MarketId, MarketType, ModifyOrderParams, RpcSendTransactionConfig, SdkError, - SdkResult, VersionedMessage, + VersionedMessage, }, AccountProvider, DriftClient, Pubkey, RpcAccountProvider, TransactionBuilder, Wallet, }; -use futures_util::{stream::FuturesUnordered, StreamExt}; use log::{debug, warn}; use rust_decimal::Decimal; use solana_client::{client_error::ClientErrorKind, rpc_config::RpcTransactionConfig}; use solana_sdk::signature::Signature; use solana_transaction_status::{option_serializer::OptionSerializer, UiTransactionEncoding}; -use std::{borrow::Cow, str::FromStr, sync::Arc}; use thiserror::Error; -use crate::types::UserCollateralResponse; use crate::{ types::{ get_market_decimals, AllMarketsResponse, CancelAndPlaceRequest, CancelOrdersRequest, GetOrdersRequest, GetOrdersResponse, GetPositionsRequest, GetPositionsResponse, Market, MarketInfoResponse, ModifyOrdersRequest, Order, PerpPosition, PerpPositionExtended, PlaceOrdersRequest, SolBalanceResponse, SpotPosition, TxEventsResponse, TxResponse, - UserLeverageResponse, UserMarginResponse, PRICE_DECIMALS, + UserCollateralResponse, UserLeverageResponse, UserMarginResponse, PRICE_DECIMALS, }, websocket::map_drift_event_for_account, Context, LOG_TARGET, @@ -96,10 +97,14 @@ impl AppState { }; let account_provider = RpcAccountProvider::with_commitment(endpoint, state_commitment); - let client = DriftClient::new(context, account_provider, wallet.clone()) + let mut client = DriftClient::new(context, account_provider, wallet.clone()) .await .expect("ok"); client.subscribe().await.expect("subd onchain data"); + client + .add_user(default_subaccount_id.unwrap_or(0)) + .await + .expect("user sub"); Self { delegated: wallet.is_delegated(), @@ -137,16 +142,20 @@ impl AppState { req: CancelOrdersRequest, ) -> GatewayResult { let sub_account = self.resolve_sub_account(ctx.sub_account_id); - let (account_data, pf) = tokio::join!( - self.client.get_user_account(&sub_account), - get_priority_fee(&self.client) - ); + let account_data = match self + .client + .get_user(ctx.sub_account_id.unwrap_or(self.default_subaccount_id)) + { + Some(user) => user.get_user_account(), + None => self.client.get_user_account(&sub_account).await?, + }; + let pf = get_priority_fee(&self.client).await; let priority_fee = ctx.cu_price.unwrap_or(pf); debug!(target: LOG_TARGET, "priority_fee: {priority_fee:?}"); let builder = TransactionBuilder::new( self.client.program_data(), sub_account, - Cow::Owned(account_data?), + Cow::Owned(account_data), self.delegated, ) .with_priority_fee(priority_fee, ctx.cu_limit); @@ -154,53 +163,57 @@ impl AppState { self.send_tx(tx, "cancel_orders").await } - /// Return orders by position if given, otherwise return all positions + /// Return position for market if given, otherwise return all positions pub async fn get_positions( &self, ctx: Context, req: Option, ) -> GatewayResult { - let (all_spot, all_perp) = self + let sub_account = self.resolve_sub_account(ctx.sub_account_id); + let user = match self .client - .all_positions(&self.resolve_sub_account(ctx.sub_account_id)) - .await?; - - // calculating spot token balance requires knowing the 'spot market account' data - let mut filtered_spot_positions = Vec::::with_capacity(all_spot.len()); - let mut filtered_spot_futs = FuturesUnordered::from_iter( - all_spot - .iter() - .filter(|p| { - if let Some(GetPositionsRequest { ref market }) = req { - p.market_index == market.market_index - && MarketType::Spot == market.market_type - } else { - true - } - }) - .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?); - } + .get_user(ctx.sub_account_id.unwrap_or(self.default_subaccount_id)) + { + Some(user) => user.get_user_account(), + None => self.client.get_user_account(&sub_account).await?, + }; + + let spot_positions = user + .spot_positions + .iter() + .filter(|p| { + !p.is_available() + || req.as_ref().is_some_and(|r| { + r.market.market_index == p.market_index + && r.market.market_type == MarketType::Perp + }) + }) + .map(|x| { + // calculating spot token balance requires knowing the 'spot market account' data + let spot_market_info = self + .client + .get_spot_market_account(x.market_index) + .expect("spot market exists"); + SpotPosition::from_sdk_type(x, &spot_market_info) + }) + .collect(); + + let perp_positions = user + .perp_positions + .into_iter() + .filter(|p| { + !p.is_available() + || req.as_ref().is_some_and(|r| { + r.market.market_index == p.market_index + && r.market.market_type == MarketType::Perp + }) + }) + .map(Into::into) + .collect(); Ok(GetPositionsResponse { - spot: filtered_spot_positions, - perp: all_perp - .iter() - .filter(|p| { - if let Some(GetPositionsRequest { ref market }) = req { - p.market_index == market.market_index - && MarketType::Perp == market.market_type - } else { - true - } - }) - .map(|x| (*x).into()) - .collect(), + spot: spot_positions, + perp: perp_positions, }) } @@ -245,28 +258,35 @@ impl AppState { market: Market, ) -> GatewayResult { let sub_account = self.resolve_sub_account(ctx.sub_account_id); - let (perp_position, user, oracle) = tokio::join!( - self.client.perp_position(&sub_account, market.market_index), - self.client.get_user_account(&sub_account), - self.client - .oracle_price(MarketId::perp(market.market_index)), - ); + let user = match self + .client + .get_user(ctx.sub_account_id.unwrap_or(self.default_subaccount_id)) + { + Some(user) => user.get_user_account(), + None => self.client.get_user_account(&sub_account).await?, + }; + let perp_position = user.get_perp_position(market.market_index); + + let oracle = self + .client + .get_oracle_price_data_and_slot_for_perp_market(market.market_index) + .expect("oracle"); - if let Some(perp_position) = perp_position? { + if let Ok(perp_position) = perp_position { let result = calculate_liquidation_price_and_unrealized_pnl( &self.client, - &user?, + &user, market.market_index, )?; - let oracle_price = oracle?; - let unsettled_pnl = Decimal::new( + let oracle_price = oracle.data.price; + let unsettled_pnl = Decimal::from_i128_with_scale( perp_position .get_unrealized_pnl(oracle_price) - .unwrap_or_default() as i64, + .unwrap_or_default(), PRICE_DECIMALS, ); - let mut p: PerpPosition = perp_position.into(); + let mut p: PerpPosition = (*perp_position).into(); p.set_extended_info(PerpPositionExtended { liquidation_price: Decimal::new(result.liquidation_price, PRICE_DECIMALS), unrealized_pnl: Decimal::new(result.unrealized_pnl as i64, PRICE_DECIMALS), @@ -287,7 +307,21 @@ impl AppState { req: Option, ) -> GatewayResult { let sub_account = self.resolve_sub_account(ctx.sub_account_id); - let orders = self.client.all_orders(&sub_account).await?; + let user = match self + .client + .get_user(ctx.sub_account_id.unwrap_or(self.default_subaccount_id)) + { + Some(user) => user.get_user_account(), + None => self.client.get_user_account(&sub_account).await?, + }; + + // TODO: export SDK type + let orders: Vec = user + .orders + .into_iter() + .filter(|o| o.status as u8 == 1) + .collect(); + Ok(GetOrdersResponse { orders: orders .into_iter() @@ -349,15 +383,19 @@ impl AppState { .collect(); let sub_account = self.resolve_sub_account(ctx.sub_account_id); - let (account_data, pf) = tokio::join!( - self.client.get_user_account(&sub_account), - get_priority_fee(&self.client) - ); + let account_data = match self + .client + .get_user(ctx.sub_account_id.unwrap_or(self.default_subaccount_id)) + { + Some(user) => user.get_user_account(), + None => self.client.get_user_account(&sub_account).await?, + }; + let pf = get_priority_fee(&self.client).await; let builder = TransactionBuilder::new( self.client.program_data(), sub_account, - Cow::Owned(account_data?), + Cow::Owned(account_data), self.delegated, ) .with_priority_fee(ctx.cu_price.unwrap_or(pf), ctx.cu_limit); @@ -411,15 +449,19 @@ impl AppState { req: ModifyOrdersRequest, ) -> GatewayResult { let sub_account = self.resolve_sub_account(ctx.sub_account_id); - let (account_data, pf) = tokio::join!( - self.client.get_user_account(&sub_account), - get_priority_fee(&self.client) - ); + let account_data = match self + .client + .get_user(ctx.sub_account_id.unwrap_or(self.default_subaccount_id)) + { + Some(user) => user.get_user_account(), + None => self.client.get_user_account(&sub_account).await?, + }; + let pf = get_priority_fee(&self.client).await; let builder = TransactionBuilder::new( self.client.program_data(), sub_account, - Cow::Owned(account_data?), + Cow::Owned(account_data), self.delegated, ) .with_priority_fee(ctx.cu_price.unwrap_or(pf), ctx.cu_limit); diff --git a/src/main.rs b/src/main.rs index d237292..5d4e585 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +use std::{borrow::Borrow, str::FromStr, sync::Arc, time::Duration}; + use actix_web::{ delete, get, middleware::Logger, @@ -6,12 +8,10 @@ use actix_web::{ App, Either, HttpResponse, HttpServer, Responder, }; use argh::FromArgs; -use log::{debug, info, warn}; - use controller::{create_wallet, AppState, ControllerError}; use drift_sdk::{types::CommitmentConfig, Pubkey}; +use log::{debug, info, warn}; use serde_json::json; -use std::{borrow::Borrow, str::FromStr, sync::Arc, time::Duration}; use types::{ CancelAndPlaceRequest, CancelOrdersRequest, Market, ModifyOrdersRequest, PlaceOrdersRequest, }; @@ -390,10 +390,7 @@ struct GatewayConfig { mod tests { use actix_web::{http::Method, test, App}; - use crate::types::Market; - use self::controller::create_wallet; - use super::*; const TEST_ENDPOINT: &str = "https://api.devnet.solana.com"; diff --git a/src/types.rs b/src/types.rs index 6483425..a11dd25 100644 --- a/src/types.rs +++ b/src/types.rs @@ -2,11 +2,9 @@ //! - 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}, - math::liquidation::MarginRequirementInfo, + constants::{ProgramData, BASE_PRECISION, PRICE_PRECISION, QUOTE_PRECISION}, + math::liquidation::{CollateralInfo, MarginRequirementInfo}, types::{ self as sdk_types, MarketPrecision, MarketType, ModifyOrderParams, OrderParams, PerpMarket, PositionDirection, PostOnlyParam, SpotMarket, @@ -571,15 +569,15 @@ impl From for UserCollateralResponse { #[cfg(test)] mod tests { + use std::str::FromStr; + use drift_sdk::{ constants::BASE_PRECISION, types::{MarketType, OrderType, PositionDirection}, }; - use std::str::FromStr; - - use crate::types::{Market, ModifyOrder, Order}; use super::{Decimal, PlaceOrder}; + use crate::types::{Market, ModifyOrder, Order}; #[test] fn place_order_to_order() {