use crate::account::outcomes::Fill;
use crate::client::{ApiError, Transport, info, user_request};
use crate::trade::Side;
use crate::utils::{Address, Dec, Round};
use serde::Deserialize;
#[must_use]
pub fn user_fees_request(user: Address) -> String {
user_request("userFees", user)
}
pub async fn user_fees(transport: &impl Transport, user: Address) -> Result<UserFees, ApiError> {
info(transport, user_fees_request(user)).await
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
pub struct UserFees {
#[serde(rename = "userSpotCrossRate")]
pub spot_cross: Dec,
#[serde(rename = "userSpotAddRate")]
pub spot_add: Dec,
#[serde(default, rename = "activeReferralDiscount")]
pub referral_discount: Dec,
}
#[must_use]
pub fn multiplier(scale: Dec) -> Option<Dec> {
scale.checked_add(scale.max(Dec::ONE))
}
#[must_use]
pub fn deployer_share(fee: Dec, scale: Dec) -> Option<Dec> {
fee.checked_mul(scale)?.checked_div(multiplier(scale)?, 8, Round::Down)
}
impl UserFees {
#[must_use]
pub fn rate(&self, crossed: bool, scale: Dec) -> Option<Dec> {
let base = if crossed { self.spot_cross } else { self.spot_add };
let kept = Dec::ONE.checked_sub(self.referral_discount)?;
base.checked_mul(multiplier(scale)?)?.checked_mul(kept)
}
#[must_use]
pub fn fee(&self, side: Side, crossed: bool, scale: Dec, notional: Dec) -> Option<Dec> {
match side {
Side::Buy => Some(Dec::ZERO),
Side::Sell => self.rate(crossed, scale)?.checked_mul(notional),
}
}
#[must_use]
pub fn fee_of(&self, fill: &Fill, scale: Dec) -> Option<Dec> {
self.fee(fill.side, fill.crossed, scale, fill.notional())
}
}
#[cfg(test)]
#[path = "tests/fee.rs"]
mod tests;