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#[repr(u8)]
11#[num_enum(error_type(name = ProgramError, constructor = Role::unknown_role))]
12pub enum Role {
13 /// The account authorized to add and remove solvers and to transfer roles.
14 /// It is the highest authority: it may transfer any role.
15 Manager = 0,
16 /// The account authorized to close buffer accounts and reclaim their rent,
17 /// choosing where that rent goes.
18 ReclaimAuthority,
19 /// The account authorized to place orders that sell the protocol's own
20 /// buffer balances (e.g., fee withdrawals). It chooses the destination, the
21 /// bought token, and the price of those orders.
22 SelfOrderAuthority,
23}
24
25impl Role {
26 /// Every [`Role`] variant, in discriminant order.
27 pub const ALL: [Self; 3] = [
28 Role::Manager,
29 Role::ReclaimAuthority,
30 Role::SelfOrderAuthority,
31 ];
32
33 /// The single wire byte that selects this role in the authority-transfer
34 /// instruction.
35 pub fn discriminator(self) -> u8 {
36 self as u8
37 }
38
39 fn unknown_role(_: u8) -> ProgramError {
40 ProgramError::InvalidInstructionData
41 }
42}
43
44#[cfg(test)]
45mod tests {
46 use super::*;
47
48 #[test]
49 fn role_try_from_partitions_all_bytes() {
50 for i in u8::MIN..=u8::MAX {
51 match Role::try_from(i) {
52 Ok(role) => assert_eq!(role as u8, i),
53 Err(err) => assert_eq!(err, ProgramError::InvalidInstructionData),
54 }
55 }
56 }
57
58 #[test]
59 fn role_try_from_matches_manager() {
60 assert_eq!(Role::try_from(0), Ok(Role::Manager));
61 }
62
63 #[test]
64 fn all_roles_lists_every_role_in_discriminator_order() {
65 // The roles `try_from` accepts, discovered independently of `Role::ALL`.
66 // The scan runs over ascending bytes, so this is every role that exists,
67 // in discriminant order.
68 let every_role: Vec<Role> = (u8::MIN..=u8::MAX)
69 .filter_map(|byte| Role::try_from(byte).ok())
70 .collect();
71
72 assert_eq!(Role::ALL.as_slice(), every_role.as_slice());
73 }
74}