Skip to content

Commit

Permalink
feat: Add Vaults REST API (#291)
Browse files Browse the repository at this point in the history
Adds Vaults REST API (3 methods). An example is also provided. The
provided tests test if the Indexer responses are correctly parsed.
  • Loading branch information
v0-e authored Nov 25, 2024
1 parent 6370149 commit f0ee3dd
Show file tree
Hide file tree
Showing 8 changed files with 221 additions and 0 deletions.
1 change: 1 addition & 0 deletions v4-client-rs/client/examples/support/constants.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
#[allow(dead_code)]
pub const TEST_MNEMONIC: &str = "mirror actor skill push coach wait confirm orchard lunch mobile athlete gossip awake miracle matter bus reopen team ladder lazy list timber render wait";
43 changes: 43 additions & 0 deletions v4-client-rs/client/examples/vault_endpoint.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
mod support;
use anyhow::{Error, Result};
use dydx::config::ClientConfig;
use dydx::indexer::{IndexerClient, PnlTickInterval};

pub struct Rester {
indexer: IndexerClient,
}

impl Rester {
pub async fn connect() -> Result<Self> {
let config = ClientConfig::from_file("client/tests/testnet.toml").await?;
let indexer = IndexerClient::new(config.indexer);
Ok(Self { indexer })
}
}

#[tokio::main]
async fn main() -> Result<()> {
tracing_subscriber::fmt().try_init().map_err(Error::msg)?;
let rester = Rester::connect().await?;
let indexer = rester.indexer;

// Test values
let resolution = PnlTickInterval::Hour;

let pnls = indexer
.vaults()
.get_megavault_historical_pnl(resolution)
.await?;
tracing::info!("MegaVault historical PnLs: {pnls:?}");

let vaults_pnls = indexer
.vaults()
.get_vaults_historical_pnl(resolution)
.await?;
tracing::info!("Vaults historical PnLs: {vaults_pnls:?}");

let positions = indexer.vaults().get_megavault_positions().await?;
tracing::info!("MegaVault positions: {positions:?}");

Ok(())
}
5 changes: 5 additions & 0 deletions v4-client-rs/client/src/indexer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ impl IndexerClient {
self.rest.utility()
}

/// Get vaults query dispatcher.
pub fn vaults(&self) -> rest::Vaults {
self.rest.vaults()
}

/// Get feeds dispatcher.
pub fn feed(&mut self) -> Feeds<'_> {
Feeds::new(&mut self.sock)
Expand Down
7 changes: 7 additions & 0 deletions v4-client-rs/client/src/indexer/rest/client/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod accounts;
pub mod markets;
pub mod utility;
pub mod vaults;

use super::config::RestConfig;
use super::options::*;
Expand All @@ -10,6 +11,7 @@ use markets::Markets;
use reqwest::Client;
use serde::Serialize;
use utility::Utility;
use vaults::Vaults;

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
Expand Down Expand Up @@ -55,4 +57,9 @@ impl RestClient {
pub(crate) fn utility(&self) -> Utility<'_> {
Utility::new(self)
}

/// Get vaults query dispatcher.
pub(crate) fn vaults(&self) -> Vaults<'_> {
Vaults::new(self)
}
}
75 changes: 75 additions & 0 deletions v4-client-rs/client/src/indexer/rest/client/vaults.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use super::*;
use anyhow::Error;

/// Vaults dispatcher.
///
/// Check [the example](https://github.com/NethermindEth/dydx-v4-rust/blob/trunk/client/examples/vaults_endpoint.rs).
pub struct Vaults<'a> {
rest: &'a RestClient,
}

impl<'a> Vaults<'a> {
/// Create a new vaults dispatcher.
pub(crate) fn new(rest: &'a RestClient) -> Self {
Self { rest }
}

/// MegaVault historical PnL.
pub async fn get_megavault_historical_pnl(
&self,
resolution: PnlTickInterval,
) -> Result<Vec<PnlTicksResponseObject>, Error> {
let rest = &self.rest;
const URI: &str = "/v4/vault/v1/megavault/historicalPnl";
let url = format!("{}{URI}", rest.config.endpoint);
let resp = rest
.client
.get(url)
.query(&[("resolution", resolution)])
.send()
.await?
.error_for_status()?
.json::<MegaVaultHistoricalPnlResponse>()
.await?
.megavault_pnl;
Ok(resp)
}

/// Vaults historical PnL.
pub async fn get_vaults_historical_pnl(
&self,
resolution: PnlTickInterval,
) -> Result<Vec<VaultHistoricalPnl>, Error> {
let rest = &self.rest;
const URI: &str = "/v4/vault/v1/vaults/historicalPnl";
let url = format!("{}{URI}", rest.config.endpoint);
let resp = rest
.client
.get(url)
.query(&[("resolution", resolution)])
.send()
.await?
.error_for_status()?
.json::<VaultsHistoricalPnLResponse>()
.await?
.vaults_pnl;
Ok(resp)
}

/// MegaVault positions.
pub async fn get_megavault_positions(&self) -> Result<Vec<VaultPosition>, Error> {
let rest = &self.rest;
const URI: &str = "/v4/vault/v1/megavault/positions";
let url = format!("{}{URI}", rest.config.endpoint);
let resp = rest
.client
.get(url)
.send()
.await?
.error_for_status()?
.json::<MegaVaultPositionResponse>()
.await?
.positions;
Ok(resp)
}
}
1 change: 1 addition & 0 deletions v4-client-rs/client/src/indexer/rest/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ pub use types::*;
pub use client::accounts::Accounts;
pub use client::markets::Markets;
pub use client::utility::Utility;
pub use client::vaults::Vaults;
60 changes: 60 additions & 0 deletions v4-client-rs/client/src/indexer/rest/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,18 @@ pub struct ErrorMsg {
#[derive(Deserialize, Debug, Clone, From, Display, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PnlTickId(pub String);

/// PnL tick resolution.
#[derive(
Deserialize, Serialize, Debug, Clone, Copy, From, Display, PartialEq, Eq, PartialOrd, Ord, Hash,
)]
#[serde(rename_all = "lowercase")]
pub enum PnlTickInterval {
/// Hour.
Hour,
/// Day.
Day,
}

/// Transfer id.
#[derive(
Serialize, Deserialize, Debug, Clone, From, Display, PartialEq, Eq, PartialOrd, Ord, Hash,
Expand Down Expand Up @@ -301,3 +313,51 @@ pub struct HistoricalTradingRewardAggregation {
/// Aggregation period.
pub period: TradingRewardAggregationPeriod,
}

/// MegaVault Profit and loss reports.
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct MegaVaultHistoricalPnlResponse {
/// List of PnL reports.
pub megavault_pnl: Vec<PnlTicksResponseObject>,
}

/// MegaVault Profit and loss reports.
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct MegaVaultPositionResponse {
/// List MegaVault positions.
pub positions: Vec<VaultPosition>,
}

/// Vaults profit and loss reports.
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct VaultsHistoricalPnLResponse {
/// List of PnL reports.
pub vaults_pnl: Vec<VaultHistoricalPnl>,
}

/// Vault Profit and loss reports.
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct VaultHistoricalPnl {
/// Associated ticker.
pub ticker: String,
/// List of PnL reports.
pub historical_pnl: Vec<PnlTicksResponseObject>,
}

/// Vault position.
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct VaultPosition {
/// Associated ticker.
pub ticker: String,
/// Asset position.
pub asset_position: Option<AssetPositionResponseObject>,
/// Perpetual position.
pub perpetual_position: Option<PerpetualPositionResponseObject>,
/// Equity.
pub equity: BigDecimal,
}
29 changes: 29 additions & 0 deletions v4-client-rs/client/tests/test_indexer_rest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,35 @@ async fn test_indexer_account_get_rewards_aggregated() -> Result<()> {
Ok(())
}

#[tokio::test]
async fn test_indexer_vaults_get_megavault_historical_pnl() -> Result<()> {
let env = TestEnv::testnet().await?;
let resolution = PnlTickInterval::Hour;
env.indexer
.vaults()
.get_megavault_historical_pnl(resolution)
.await?;
Ok(())
}

#[tokio::test]
async fn test_indexer_vaults_get_vaults_historical_pnl() -> Result<()> {
let env = TestEnv::testnet().await?;
let resolution = PnlTickInterval::Hour;
env.indexer
.vaults()
.get_vaults_historical_pnl(resolution)
.await?;
Ok(())
}

#[tokio::test]
async fn test_indexer_vaults_get_megavault_positions() -> Result<()> {
let env = TestEnv::testnet().await?;
env.indexer.vaults().get_megavault_positions().await?;
Ok(())
}

#[tokio::test]
async fn test_perpetual_market_quantization() -> Result<()> {
let env = TestEnv::testnet().await?;
Expand Down

0 comments on commit f0ee3dd

Please sign in to comment.