spl_token_group_interface/
state.rs1use {
4 crate::error::TokenGroupError,
5 bytemuck::{Pod, Zeroable},
6 miraland_program::{program_error::ProgramError, pubkey::Pubkey},
7 spl_discriminator::SplDiscriminate,
8 spl_pod::{error::PodSliceError, optional_keys::OptionalNonZeroPubkey, primitives::PodU32},
9};
10
11#[repr(C)]
13#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable, SplDiscriminate)]
14#[discriminator_hash_input("spl_token_group_interface:group")]
15pub struct TokenGroup {
16 pub update_authority: OptionalNonZeroPubkey,
18 pub mint: Pubkey,
21 pub size: PodU32,
23 pub max_size: PodU32,
25}
26
27impl TokenGroup {
28 pub fn new(mint: &Pubkey, update_authority: OptionalNonZeroPubkey, max_size: u32) -> Self {
30 Self {
31 mint: *mint,
32 update_authority,
33 size: PodU32::default(), max_size: max_size.into(),
35 }
36 }
37
38 pub fn update_max_size(&mut self, new_max_size: u32) -> Result<(), ProgramError> {
40 if new_max_size < u32::from(self.size) {
42 return Err(TokenGroupError::SizeExceedsNewMaxSize.into());
43 }
44 self.max_size = new_max_size.into();
45 Ok(())
46 }
47
48 pub fn increment_size(&mut self) -> Result<u32, ProgramError> {
50 let new_size = u32::from(self.size)
52 .checked_add(1)
53 .ok_or::<ProgramError>(PodSliceError::CalculationFailure.into())?;
54 if new_size > u32::from(self.max_size) {
55 return Err(TokenGroupError::SizeExceedsMaxSize.into());
56 }
57 self.size = new_size.into();
58 Ok(new_size)
59 }
60}
61
62#[repr(C)]
64#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable, SplDiscriminate)]
65#[discriminator_hash_input("spl_token_group_interface:member")]
66pub struct TokenGroupMember {
67 pub mint: Pubkey,
70 pub group: Pubkey,
72 pub member_number: PodU32,
74}
75impl TokenGroupMember {
76 pub fn new(mint: &Pubkey, group: &Pubkey, member_number: u32) -> Self {
78 Self {
79 mint: *mint,
80 group: *group,
81 member_number: member_number.into(),
82 }
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use {
89 super::*,
90 crate::NAMESPACE,
91 miraland_program::hash,
92 spl_discriminator::ArrayDiscriminator,
93 spl_type_length_value::state::{TlvState, TlvStateBorrowed, TlvStateMut},
94 std::mem::size_of,
95 };
96
97 #[test]
98 fn discriminators() {
99 let preimage = hash::hashv(&[format!("{NAMESPACE}:group").as_bytes()]);
100 let discriminator =
101 ArrayDiscriminator::try_from(&preimage.as_ref()[..ArrayDiscriminator::LENGTH]).unwrap();
102 assert_eq!(TokenGroup::SPL_DISCRIMINATOR, discriminator);
103
104 let preimage = hash::hashv(&[format!("{NAMESPACE}:member").as_bytes()]);
105 let discriminator =
106 ArrayDiscriminator::try_from(&preimage.as_ref()[..ArrayDiscriminator::LENGTH]).unwrap();
107 assert_eq!(TokenGroupMember::SPL_DISCRIMINATOR, discriminator);
108 }
109
110 #[test]
111 fn tlv_state_pack() {
112 let group = TokenGroup {
114 mint: Pubkey::new_unique(),
115 update_authority: OptionalNonZeroPubkey::try_from(Some(Pubkey::new_unique())).unwrap(),
116 size: 10.into(),
117 max_size: 20.into(),
118 };
119
120 let member = TokenGroupMember {
121 mint: Pubkey::new_unique(),
122 group: Pubkey::new_unique(),
123 member_number: 0.into(),
124 };
125
126 let account_size = TlvStateBorrowed::get_base_len()
127 + size_of::<TokenGroup>()
128 + TlvStateBorrowed::get_base_len()
129 + size_of::<TokenGroupMember>();
130 let mut buffer = vec![0; account_size];
131 let mut state = TlvStateMut::unpack(&mut buffer).unwrap();
132
133 let group_data = state.init_value::<TokenGroup>(false).unwrap().0;
134 *group_data = group;
135
136 let member_data = state.init_value::<TokenGroupMember>(false).unwrap().0;
137 *member_data = member;
138
139 assert_eq!(state.get_first_value::<TokenGroup>().unwrap(), &group);
140 assert_eq!(
141 state.get_first_value::<TokenGroupMember>().unwrap(),
142 &member
143 );
144 }
145
146 #[test]
147 fn update_max_size() {
148 let max_size = 10;
150 let mut group = TokenGroup {
151 mint: Pubkey::new_unique(),
152 update_authority: OptionalNonZeroPubkey::try_from(Some(Pubkey::new_unique())).unwrap(),
153 size: 0.into(),
154 max_size: max_size.into(),
155 };
156
157 let new_max_size = 30;
158 group.update_max_size(new_max_size).unwrap();
159 assert_eq!(u32::from(group.max_size), new_max_size);
160
161 group.size = 30.into();
163
164 let new_max_size = 20;
166 assert_eq!(
167 group.update_max_size(new_max_size),
168 Err(ProgramError::from(TokenGroupError::SizeExceedsNewMaxSize))
169 );
170
171 let new_max_size = 30;
172 group.update_max_size(new_max_size).unwrap();
173 assert_eq!(u32::from(group.max_size), new_max_size);
174 }
175
176 #[test]
177 fn increment_current_size() {
178 let mut group = TokenGroup {
179 mint: Pubkey::new_unique(),
180 update_authority: OptionalNonZeroPubkey::try_from(Some(Pubkey::new_unique())).unwrap(),
181 size: 0.into(),
182 max_size: 1.into(),
183 };
184
185 group.increment_size().unwrap();
186 assert_eq!(u32::from(group.size), 1);
187
188 assert_eq!(
190 group.increment_size(),
191 Err(ProgramError::from(TokenGroupError::SizeExceedsMaxSize))
192 );
193 }
194}