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.
25 SettlementOwnedOrderAuthority,
26}
27
28impl Role {
29 /// Every [`Role`] variant, in discriminant order.
30 pub const ALL: [Self; 4] = [
31 Role::Manager,
32 Role::SolverAuthority,
33 Role::ReclaimAuthority,
34 Role::SettlementOwnedOrderAuthority,
35 ];
36
37 /// The single wire byte that selects this role in the authority-transfer
38 /// instruction.
39 pub fn discriminator(self) -> u8 {
40 self as u8
41 }
42
43 fn unknown_role(_: u8) -> ProgramError {
44 ProgramError::InvalidInstructionData
45 }
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51
52 #[test]
53 fn role_try_from_partitions_all_bytes() {
54 for i in u8::MIN..=u8::MAX {
55 match Role::try_from(i) {
56 Ok(role) => assert_eq!(role as u8, i),
57 Err(err) => assert_eq!(err, ProgramError::InvalidInstructionData),
58 }
59 }
60 }
61
62 #[test]
63 fn role_try_from_matches_manager() {
64 assert_eq!(Role::try_from(0), Ok(Role::Manager));
65 }
66
67 #[test]
68 fn all_roles_lists_every_role_in_discriminator_order() {
69 // The roles `try_from` accepts, discovered independently of `Role::ALL`.
70 // The scan runs over ascending bytes, so this is every role that exists,
71 // in discriminant order.
72 let every_role: Vec<Role> = (u8::MIN..=u8::MAX)
73 .filter_map(|byte| Role::try_from(byte).ok())
74 .collect();
75
76 assert_eq!(Role::ALL.as_slice(), every_role.as_slice());
77 }
78}