1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
use anchor_lang::prelude::*;

use crate::{orderbook::state::OrderTag, FixedTermErrorCode};

/// A `ClaimTicket` represents a claim of tickets that have been staked with the program
/// This account is generated by the `StakeTickets` program instruction
#[account]
#[derive(Debug)]
pub struct ClaimTicket {
    /// The account registered as owner of this claim
    pub owner: Pubkey,
    /// The `TicketManager` this claim ticket was established under
    /// Determines the asset this ticket will be redeemed for
    pub market: Pubkey,
    /// The slot after which this claim can be redeemed for the underlying value
    pub maturation_timestamp: i64,
    /// The number of tokens this claim  is redeemable for
    pub redeemable: u64,
}

/// A split ticket represents a claim of underlying tokens as the result of a lending action.
///
/// The split ticket is generated when a user places a matched order with the `auto_stake` flag set to true.
/// By taking the difference between the matched base and quote quantities, the split ticket assigns principal and
/// interest values.
#[account]
#[derive(Debug)]
pub struct SplitTicket {
    /// The account registered as owner of this claim
    pub owner: Pubkey,
    /// The `TicketManager` this claim ticket was established under
    /// Determines the asset this ticket will be redeemed for
    pub market: Pubkey,
    /// The `OrderTag` associated with the creation of this struct
    pub order_tag: OrderTag,
    /// The time slot during which the ticket was struck
    pub struck_timestamp: i64,
    /// The slot after which this claim can be redeemed for the underlying value
    pub maturation_timestamp: i64,
    /// The total number of principal tokens the ticket was struck for
    pub principal: u64,
    /// The total number of interest tokens struck for this ticket
    /// same underlying asset as the principal token
    pub interest: u64,
}

impl SplitTicket {
    pub fn make_seeds<'a>(user: &'a [u8], bytes: &'a [u8]) -> [&'a [u8]; 3] {
        [crate::seeds::SPLIT_TICKET, user, bytes]
    }
}

/// Enum used for pattern matching a ticket deserialization
pub(crate) enum TicketKind<'info> {
    Claim(Account<'info, ClaimTicket>),
    Split(Account<'info, SplitTicket>),
}

/// Deserializes an ambiguous `AccountInfo` into the correct `TicketKind`
pub(crate) fn deserialize_ticket(info: AccountInfo) -> Result<TicketKind> {
    if let Ok(ticket) = Account::<ClaimTicket>::try_from(&info) {
        return Ok(TicketKind::Claim(ticket));
    } else if let Ok(ticket) = Account::<SplitTicket>::try_from(&info) {
        return Ok(TicketKind::Split(ticket));
    }

    err!(FixedTermErrorCode::FailedToDeserializeTicket)
}

make_verification!(ClaimTicket);
make_verification!(SplitTicket);

macro_rules! make_verification {
    ($ticket:ty) => {
        impl $ticket {
            /// Verify ticket ownership, takes owner and manager pubkeys
            pub fn verify_owner_manager(&self, owner: &Pubkey, market: &Pubkey) -> Result<()> {
                if self.owner != *owner {
                    return err!(FixedTermErrorCode::DoesNotOwnTicket);
                }
                if self.market != *market {
                    return err!(FixedTermErrorCode::TicketNotFromManager);
                }

                Ok(())
            }

            /// Verify ticket ownership, takes owner pubkey
            pub fn verify_owner(&self, owner: &Pubkey) -> Result<()> {
                if self.owner != *owner {
                    return err!(FixedTermErrorCode::DoesNotOwnTicket);
                }

                Ok(())
            }
        }
    };
}
pub(crate) use make_verification;