Skip to main content

solmail_program/state/
escrow.rs

1use crate::error::SolMailError;
2
3/// Size of the EscrowOrder account in bytes
4pub const ESCROW_ORDER_SIZE: usize = 75;
5
6/// Escrow order state - holds payment until delivery confirmed
7#[repr(C, packed)]
8#[derive(Clone, Copy, Debug)]
9pub struct EscrowOrder {
10    /// Public key of the payer who created this escrow
11    pub payer: [u8; 32],
12    /// Amount in lamports (SOL) or token units (USDC)
13    pub amount: u64,
14    /// Unique 128-bit mail order ID (split into two u64s)
15    pub mail_id: [u64; 2],
16    /// Unix timestamp when escrow was created
17    pub created_at: i64,
18    /// Unix timestamp after which refund is allowed
19    pub timeout_at: i64,
20    /// Payment type: false = SOL, true = USDC
21    pub is_usdc: bool,
22    /// Whether delivery has been confirmed
23    pub delivered: bool,
24    /// PDA bump seed for this escrow
25    pub bump: u8,
26}
27
28const _: () = assert!(core::mem::size_of::<EscrowOrder>() == ESCROW_ORDER_SIZE);
29
30impl EscrowOrder {
31    /// Zero-copy parse from account data
32    pub fn from_bytes(data: &[u8]) -> Result<&Self, SolMailError> {
33        if data.len() < ESCROW_ORDER_SIZE {
34            return Err(SolMailError::InvalidInstructionData);
35        }
36        // SAFETY: Data is properly sized and repr(C, packed) ensures predictable layout
37        Ok(unsafe { &*(data.as_ptr() as *const Self) })
38    }
39
40    /// Zero-copy mutable parse from account data
41    pub fn from_bytes_mut(data: &mut [u8]) -> Result<&mut Self, SolMailError> {
42        if data.len() < ESCROW_ORDER_SIZE {
43            return Err(SolMailError::InvalidInstructionData);
44        }
45        // SAFETY: Data is properly sized and repr(C, packed) ensures predictable layout
46        Ok(unsafe { &mut *(data.as_mut_ptr() as *mut Self) })
47    }
48
49    /// Get the 128-bit mail_id as bytes (for PDA derivation)
50    pub fn mail_id_bytes(&self) -> [u8; 16] {
51        let mut bytes = [0u8; 16];
52        bytes[..8].copy_from_slice(&self.mail_id[0].to_le_bytes());
53        bytes[8..].copy_from_slice(&self.mail_id[1].to_le_bytes());
54        bytes
55    }
56
57    /// Check if the escrow has timed out
58    pub fn is_timed_out(&self, current_time: i64) -> bool {
59        current_time >= self.timeout_at
60    }
61}