use crate::trade::error::{TradeError, units};
use crate::utils::json::Json;
use crate::utils::sign::goldilocks::Fp;
use crate::utils::sign::tx::{Attributes, BadTx, Tx, TxType, element, number};
use crate::utils::{COLLATERAL_DECIMALS, Dec, MarketId, exact_units};
pub const MAX_MARGIN_UNITS: u64 = (1 << 60) - 1;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MarginDirection {
Remove,
Add,
}
impl MarginDirection {
#[must_use]
pub const fn code(self) -> u8 {
match self {
MarginDirection::Remove => 0,
MarginDirection::Add => 1,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct UpdateMargin {
pub market: MarketId,
pub amount: u64,
pub direction: MarginDirection,
pub attributes: Attributes,
}
impl UpdateMargin {
pub fn add(market: MarketId, amount: Dec) -> Result<UpdateMargin, TradeError> {
UpdateMargin::moving(market, amount, MarginDirection::Add)
}
pub fn remove(market: MarketId, amount: Dec) -> Result<UpdateMargin, TradeError> {
UpdateMargin::moving(market, amount, MarginDirection::Remove)
}
fn moving(market: MarketId, amount: Dec, direction: MarginDirection) -> Result<UpdateMargin, TradeError> {
let amount = units("USDCAmount", exact_units(amount, COLLATERAL_DECIMALS, MAX_MARGIN_UNITS))?;
Ok(UpdateMargin { market, amount, direction, attributes: Attributes::NONE })
}
#[must_use]
pub const fn with(mut self, attributes: Attributes) -> UpdateMargin {
self.attributes = attributes;
self
}
}
impl Tx for UpdateMargin {
fn tx_type(&self) -> TxType {
TxType::UpdateMargin
}
fn hash_fields(&self) -> Vec<Fp> {
vec![
element(self.market.get()),
element(self.amount & 0xFFFF_FFFF),
element(self.amount >> 32),
element(self.direction.code()),
]
}
fn info_fields(&self) -> Vec<(&'static str, Json)> {
vec![
("MarketIndex", number(self.market.get())),
("USDCAmount", number(self.amount)),
("Direction", number(self.direction.code())),
]
}
fn attributes(&self) -> Attributes {
self.attributes
}
fn check(&self) -> Result<(), BadTx> {
self.attributes.check()?;
if self.amount == 0 {
return Err(BadTx::new("USDCAmount", "an amount is at least 1"));
}
if self.amount > MAX_MARGIN_UNITS {
return Err(BadTx::new("USDCAmount", "an amount is at most 2^60 - 1"));
}
Ok(())
}
}
#[cfg(test)]
#[path = "tests/isolated.rs"]
mod tests;