use serde::{Deserialize, Serialize};
use crate::types::MarketId;
use crate::types::order::Side;
use crate::wallet::Address;
#[derive(
Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize,
)]
#[serde(transparent)]
pub struct RfqId(pub u64);
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RfqStatus {
Open,
Accepted,
Expired,
Cancelled,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct MmQuote {
pub rfq_id: RfqId,
pub mm: Address,
pub price: u64,
pub size: u64,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct RfqState {
pub rfq_id: RfqId,
pub taker: Address,
pub market: MarketId,
pub side: Side,
pub size: u64,
pub expires_at_ms: u64,
pub quotes: Vec<MmQuote>,
pub status: RfqStatus,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum CoreSide {
Bid,
Ask,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct RfqRequest {
pub market: MarketId,
pub side: CoreSide,
pub size: u128,
pub limit_px: Option<i128>,
pub expiry_ms: u64,
pub stp_group: Option<u64>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct RfqAccept {
pub rfq_id: RfqId,
pub quote_idx: u32,
pub size: u128,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn core_side_serializes_pascal_case() {
assert_eq!(serde_json::to_string(&CoreSide::Bid).unwrap(), "\"Bid\"");
assert_eq!(serde_json::to_string(&CoreSide::Ask).unwrap(), "\"Ask\"");
}
#[test]
fn rfq_request_keeps_optional_keys_present() {
let r = RfqRequest {
market: MarketId(7),
side: CoreSide::Bid,
size: 1_000,
limit_px: None,
expiry_ms: 0,
stp_group: None,
};
let j = serde_json::to_value(r).unwrap();
assert_eq!(j["side"], "Bid");
assert_eq!(j["market"], 7);
assert!(j.get("limit_px").is_some() && j["limit_px"].is_null());
assert!(j.get("stp_group").is_some() && j["stp_group"].is_null());
let dec: RfqRequest = serde_json::from_value(j).unwrap();
assert_eq!(dec, r);
}
#[test]
fn rfq_accept_round_trips() {
let a = RfqAccept {
rfq_id: RfqId(5),
quote_idx: 0,
size: 1_000,
};
let j = serde_json::to_value(a).unwrap();
assert_eq!(j["rfq_id"], 5);
assert_eq!(j["quote_idx"], 0);
let dec: RfqAccept = serde_json::from_value(j).unwrap();
assert_eq!(dec, a);
}
#[test]
fn rfq_status_serializes_snake_case() {
assert_eq!(serde_json::to_string(&RfqStatus::Open).unwrap(), "\"open\"");
assert_eq!(
serde_json::to_string(&RfqStatus::Accepted).unwrap(),
"\"accepted\""
);
assert_eq!(
serde_json::to_string(&RfqStatus::Cancelled).unwrap(),
"\"cancelled\""
);
}
#[test]
fn rfq_state_round_trips() {
let s = RfqState {
rfq_id: RfqId(7),
taker: Address::ZERO,
market: MarketId(1),
side: Side::Bid,
size: 1_000,
expires_at_ms: 1_700_000_000_000,
quotes: vec![MmQuote {
rfq_id: RfqId(7),
mm: Address::ZERO,
price: 100,
size: 500,
}],
status: RfqStatus::Open,
};
let j = serde_json::to_string(&s).unwrap();
let dec: RfqState = serde_json::from_str(&j).unwrap();
assert_eq!(s, dec);
}
}