use serde::Deserialize;
use crate::client::http::{ApiError, Call, Transport, read};
use crate::markets::listed;
use crate::utils::dec::Dec;
use crate::utils::figure::Figure;
use crate::utils::ids::whole;
use crate::utils::{Address, AssetId};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AssetMarginMode {
Enabled,
Disabled,
PricedOnly,
#[serde(other)]
Other,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub struct AssetDetail {
pub asset_id: AssetId,
pub symbol: String,
pub l1_decimals: u32,
pub decimals: u32,
pub min_transfer_amount: Dec,
pub min_withdrawal_amount: Dec,
pub margin_mode: AssetMarginMode,
pub index_price: Dec,
pub price_decimals: u32,
pub l1_address: Address,
pub loan_to_value: Dec,
pub liquidation_threshold: Dec,
pub liquidation_factor: Dec,
pub liquidation_fee: Dec,
pub global_supply_cap: Dec,
pub user_supply_cap: Dec,
pub total_supplied: Dec,
pub multiplier: Dec,
}
impl AssetDetail {
#[must_use]
pub fn is_margin(&self) -> bool {
self.margin_mode == AssetMarginMode::Enabled
}
#[must_use]
pub fn is_plain(&self) -> bool {
self.multiplier == Dec::ONE
}
}
#[derive(Deserialize)]
struct AssetDetails {
#[serde(default, deserialize_with = "listed")]
asset_details: Vec<AssetDetail>,
}
#[must_use]
pub fn asset_details_call(asset: Option<AssetId>) -> Call {
Call::get("assetDetails").maybe("asset_id", asset)
}
pub async fn asset_details(transport: &impl Transport) -> Result<Vec<AssetDetail>, ApiError> {
Ok(read::<AssetDetails>(transport, asset_details_call(None)).await?.asset_details)
}
pub async fn asset_detail(transport: &impl Transport, asset: AssetId) -> Result<Option<AssetDetail>, ApiError> {
let details = read::<AssetDetails>(transport, asset_details_call(Some(asset))).await?.asset_details;
Ok(details.into_iter().find(|detail| detail.asset_id == asset))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum TokenMarket {
Perps,
Spot,
#[serde(other)]
Other,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum AssetClass {
Crypto,
Rwa,
#[serde(other)]
Other,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub struct Token {
pub symbol: String,
#[serde(default)]
pub backend_symbol: Option<String>,
pub name: String,
#[serde(default)]
pub logo: String,
#[serde(default)]
pub logo_extension: String,
#[serde(default)]
pub description_key: String,
#[serde(default)]
pub gecko_id: String,
#[serde(default)]
pub paprika_id: String,
pub market: TokenMarket,
pub asset_type: AssetClass,
#[serde(default)]
pub categories: Vec<String>,
#[serde(default)]
pub is_allowed_mainnet: bool,
#[serde(default)]
pub is_asset_allowed_mainnet: bool,
#[serde(default, deserialize_with = "whole")]
pub default_order_book_group_by: u64,
}
#[derive(Deserialize)]
struct TokenList {
#[serde(default, deserialize_with = "listed")]
tokens: Vec<Token>,
}
#[must_use]
pub fn token_list_call() -> Call {
Call::get("tokenlist")
}
pub async fn token_list(transport: &impl Transport) -> Result<Vec<Token>, ApiError> {
Ok(read::<TokenList>(transport, token_list_call()).await?.tokens)
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub struct SyntheticSpot {
pub symbol: String,
pub source: String,
pub bps_per_day: Figure,
#[serde(deserialize_with = "whole")]
pub expiry_time_ms: u64,
#[serde(deserialize_with = "whole")]
pub spot_close_ms: u64,
}
#[must_use]
pub fn synthetic_spot_info_call(symbol: &str) -> Call {
Call::get("syntheticSpotInfo").field("symbol", symbol)
}
pub async fn synthetic_spot_info(transport: &impl Transport, symbol: &str) -> Result<SyntheticSpot, ApiError> {
read(transport, synthetic_spot_info_call(symbol)).await
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub struct OraclePrice {
pub source: String,
pub using_median: bool,
pub using_internal_price: bool,
#[serde(default)]
pub median_sources: Vec<String>,
pub price_cap_bps: Figure,
pub price_cap_reference_price: Figure,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub struct OracleMarket {
pub symbol: String,
#[serde(default)]
pub pre_market: bool,
pub index: OraclePrice,
pub mark: OraclePrice,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub struct OracleInfo {
#[serde(deserialize_with = "whole")]
pub computed_at_ms: u64,
#[serde(default, deserialize_with = "listed")]
pub markets: Vec<OracleMarket>,
}
#[must_use]
pub fn price_oracle_info_call(symbol: Option<&str>) -> Call {
Call::get("priceOracleInfo").maybe("symbol", symbol)
}
pub async fn price_oracle_info(transport: &impl Transport, symbol: Option<&str>) -> Result<OracleInfo, ApiError> {
read(transport, price_oracle_info_call(symbol)).await
}
#[cfg(test)]
#[path = "tests/assets.rs"]
mod tests;