use super::ChainError;
use crate::spreads::SpreadType;
use crate::types::TakerSide;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OpenSpreadEvent {
pub side: TakerSide,
pub user: [u8; 32],
pub maker_pda: [u8; 32],
pub market_pda: [u8; 32],
pub position_pda: [u8; 32],
pub position_type: SpreadType,
pub strike: u64,
pub upper_strike: u64,
pub quantity: u64,
pub price: u64,
pub valid_until: u64,
pub order_id: [u8; 32],
pub total_premium: u64,
pub created_at: u64,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SettleSpreadEvent {
pub settler: [u8; 32],
pub writer: [u8; 32],
pub holder: [u8; 32],
pub position_pda: [u8; 32],
pub market_pda: [u8; 32],
pub final_status: u8,
pub is_otm: bool,
pub holder_payoff: u64,
pub writer_return: u64,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SpreadEvent {
Open(OpenSpreadEvent),
Settle(SettleSpreadEvent),
}
pub fn decode_spread_event(data: &[u8]) -> Result<Option<SpreadEvent>, ChainError> {
if data.len() < 6 || &data[..5] != b"ACTA\x01" {
return Err(ChainError::InvalidAccountData);
}
let bytes = &data[6..];
let array = |offset: usize| -> Result<[u8; 32], ChainError> {
bytes
.get(offset..offset + 32)
.and_then(|s| s.try_into().ok())
.ok_or(ChainError::InvalidAccountData)
};
let word = |offset: usize| -> Result<u64, ChainError> {
let value = bytes
.get(offset..offset + 8)
.and_then(|s| s.try_into().ok())
.ok_or(ChainError::InvalidAccountData)?;
Ok(u64::from_le_bytes(value))
};
Ok(Some(match data[5] {
kind @ (79 | 86) => {
if bytes.len() != 217 {
return Err(ChainError::InvalidAccountData);
}
SpreadEvent::Open(OpenSpreadEvent {
side: if kind == 86 {
TakerSide::Buy
} else {
TakerSide::Sell
},
user: array(0)?,
maker_pda: array(32)?,
market_pda: array(64)?,
position_pda: array(96)?,
position_type: SpreadType::try_from(bytes[128])?,
strike: word(129)?,
quantity: word(137)?,
price: word(145)?,
valid_until: word(153)?,
order_id: array(161)?,
total_premium: word(193)?,
created_at: word(201)?,
upper_strike: word(209)?,
})
}
80 => {
if bytes.len() != 178 || bytes[160] != 4 || bytes[161] > 1 {
return Err(ChainError::InvalidAccountData);
}
SpreadEvent::Settle(SettleSpreadEvent {
settler: array(0)?,
writer: array(32)?,
holder: array(64)?,
position_pda: array(96)?,
market_pda: array(128)?,
final_status: bytes[160],
is_otm: bytes[161] == 1,
holder_payoff: word(162)?,
writer_return: word(170)?,
})
}
_ => return Ok(None),
}))
}