Skip to main content

cow_settlement_interface/
role.rs

1//! Transferable authority roles stored in the settlement state PDA.
2
3use solana_program_error::ProgramError;
4
5/// A transferable authority stored in the state PDA.
6///
7/// The discriminant is the wire value carried by the authority-transfer
8/// instruction (see [`transfer_authority`](crate::instruction::transfer_authority)).
9#[derive(Clone, Copy, Debug, Eq, PartialEq, num_enum::TryFromPrimitive)]
10#[cfg_attr(feature = "cli", derive(clap::ValueEnum))]
11#[repr(u8)]
12#[num_enum(error_type(name = ProgramError, constructor = Role::unknown_role))]
13pub enum Role {
14    /// The account authorized to transfer roles. It is the highest authority: it
15    /// may transfer any role.
16    Manager = 0,
17    /// The account authorized to add and remove solvers.
18    SolverAuthority,
19    /// The account authorized to close buffer accounts and reclaim their rent,
20    /// choosing where that rent goes.
21    ReclaimAuthority,
22    /// The account authorized to place orders that sell the protocol's own
23    /// buffer balances (e.g., fee withdrawals). It chooses the destination, the
24    /// bought token, and the price of those orders. It may also withdraw the
25    /// native SOL buffer's lamports above rent.
26    SettlementOwnedOrderAuthority,
27}
28
29impl Role {
30    /// Every [`Role`] variant, in discriminant order.
31    pub const ALL: [Self; 4] = [
32        Role::Manager,
33        Role::SolverAuthority,
34        Role::ReclaimAuthority,
35        Role::SettlementOwnedOrderAuthority,
36    ];
37
38    /// The single wire byte that selects this role in the authority-transfer
39    /// instruction.
40    pub fn discriminator(self) -> u8 {
41        self as u8
42    }
43
44    fn unknown_role(_: u8) -> ProgramError {
45        ProgramError::InvalidInstructionData
46    }
47}
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52
53    #[test]
54    fn role_try_from_partitions_all_bytes() {
55        for i in u8::MIN..=u8::MAX {
56            match Role::try_from(i) {
57                Ok(role) => assert_eq!(role as u8, i),
58                Err(err) => assert_eq!(err, ProgramError::InvalidInstructionData),
59            }
60        }
61    }
62
63    #[test]
64    fn role_try_from_matches_manager() {
65        assert_eq!(Role::try_from(0), Ok(Role::Manager));
66    }
67
68    #[test]
69    fn all_roles_lists_every_role_in_discriminator_order() {
70        // The roles `try_from` accepts, discovered independently of `Role::ALL`.
71        // The scan runs over ascending bytes, so this is every role that exists,
72        // in discriminant order.
73        let every_role: Vec<Role> = (u8::MIN..=u8::MAX)
74            .filter_map(|byte| Role::try_from(byte).ok())
75            .collect();
76
77        assert_eq!(Role::ALL.as_slice(), every_role.as_slice());
78    }
79}