use alloy::primitives::{Bytes, U256};
use fastnum::{UD64, UD128};
use super::*;
use crate::{abi::dex::Exchange::OrderDesc, error::DexError, num, state};
#[derive(Clone, Copy, Debug)]
pub enum RequestType {
OpenLong,
OpenShort,
CloseLong,
CloseShort,
Cancel,
IncreasePositionCollateral,
Change,
}
#[derive(Clone, derive_more::Debug)]
pub struct OrderRequest {
request_id: RequestId,
perp_id: PerpetualId,
r#type: RequestType,
order_id: Option<OrderId>,
#[debug("{price}")]
price: UD64,
#[debug("{size}")]
size: UD64,
expiry_block: Option<u64>,
post_only: bool,
fill_or_kill: bool,
immediate_or_cancel: bool,
max_matches: Option<u32>,
#[debug("{leverage}")]
leverage: UD64,
last_exec_block: Option<u64>,
amount: Option<UD128>,
max_neg_pnl_collat_bps: u16,
builder: Option<BuilderAttribution>,
}
impl OrderRequest {
#[allow(clippy::too_many_arguments)]
pub fn new(
request_id: RequestId,
perp_id: PerpetualId,
r#type: RequestType,
order_id: Option<OrderId>,
price: UD64,
size: UD64,
expiry_block: Option<u64>,
post_only: bool,
fill_or_kill: bool,
immediate_or_cancel: bool,
max_matches: Option<u32>,
leverage: UD64,
last_exec_block: Option<u64>,
amount: Option<UD128>,
max_neg_pnl_collat_bps: u16,
) -> Self {
Self {
request_id,
perp_id,
r#type,
order_id,
price,
size,
expiry_block,
post_only,
fill_or_kill,
immediate_or_cancel,
max_matches,
leverage,
last_exec_block,
amount,
max_neg_pnl_collat_bps,
builder: None,
}
}
pub fn with_builder(mut self, builder: BuilderAttribution) -> Self {
self.builder = Some(builder);
self
}
pub fn builder(&self) -> Option<BuilderAttribution> { self.builder }
pub fn prepare(&self, exchange: &state::Exchange) -> OrderDesc {
let perp = exchange
.perpetuals()
.get(&self.perp_id)
.expect("known perpetual");
self.to_order_desc(
perp.price_converter(),
perp.size_converter(),
perp.leverage_converter(),
Some(exchange.collateral_converter()),
)
}
pub fn prepare_v2(&self, exchange: &state::Exchange) -> Result<(OrderDesc, Bytes), DexError> {
let perp = exchange
.perpetuals()
.get(&self.perp_id)
.ok_or(DexError::PerpetualNotTracked(self.perp_id))?;
let extension = match self.builder {
None => Bytes::new(),
Some(builder) => {
if !exchange.features().builder_attribution() {
return Err(DexError::UnsupportedByContract(
"builder attribution",
exchange.features(),
));
}
builder.encode()?
},
};
Ok((
self.to_order_desc(
perp.price_converter(),
perp.size_converter(),
perp.leverage_converter(),
Some(exchange.collateral_converter()),
),
extension,
))
}
pub fn to_order_extension(&self) -> Result<Bytes, OrderExtensionError> {
self.builder
.map(|builder| builder.encode())
.transpose()
.map(Option::unwrap_or_default)
}
pub(crate) fn to_order_desc(
&self,
price_converter: num::Converter,
size_converter: num::Converter,
leverage_converter: num::Converter,
collateral_converter: Option<num::Converter>,
) -> OrderDesc {
OrderDesc {
orderDescId: U256::from(self.request_id),
perpId: U256::from(self.perp_id),
orderType: self.r#type as u8,
orderId: U256::from(self.order_id.map(|id| id.get()).unwrap_or(0)),
pricePNS: price_converter.to_unsigned(self.price),
lotLNS: size_converter.to_unsigned(self.size),
expiryBlock: U256::from(self.expiry_block.unwrap_or_default()),
postOnly: self.post_only,
fillOrKill: self.fill_or_kill,
immediateOrCancel: self.immediate_or_cancel,
maxMatches: U256::from(self.max_matches.unwrap_or_default()),
leverageHdths: leverage_converter.to_unsigned(self.leverage),
lastExecutionBlock: U256::from(self.last_exec_block.unwrap_or_default()),
amountCNS: self
.amount
.zip(collateral_converter)
.map(|(a, conv)| conv.to_unsigned(a))
.unwrap_or_default(),
maxNegPnlCollatBPS: U256::from(self.max_neg_pnl_collat_bps),
}
}
}
impl From<u8> for RequestType {
fn from(value: u8) -> Self {
match value {
0 => RequestType::OpenLong,
1 => RequestType::OpenShort,
2 => RequestType::CloseLong,
3 => RequestType::CloseShort,
4 => RequestType::Cancel,
5 => RequestType::IncreasePositionCollateral,
6 => RequestType::Change,
_ => unreachable!(),
}
}
}
impl RequestType {
pub fn try_side(&self) -> Option<OrderSide> {
match self {
RequestType::OpenLong | RequestType::CloseShort => Some(OrderSide::Bid),
RequestType::OpenShort | RequestType::CloseLong => Some(OrderSide::Ask),
_ => None,
}
}
}
impl From<RequestType> for OrderType {
fn from(value: RequestType) -> Self {
match value {
RequestType::OpenLong => OrderType::OpenLong,
RequestType::OpenShort => OrderType::OpenShort,
RequestType::CloseLong => OrderType::CloseLong,
RequestType::CloseShort => OrderType::CloseShort,
_ => unreachable!(),
}
}
}