spl_token_2022_interface/
pod.rs1#[cfg(test)]
4use crate::state::{Account, Mint, Multisig};
5use {
6 crate::{
7 instruction::MAX_SIGNERS,
8 state::{AccountState, PackedSizeOf},
9 },
10 bytemuck::{Pod, Zeroable},
11 solana_program_error::ProgramError,
12 solana_program_option::COption,
13 solana_program_pack::IsInitialized,
14 solana_pubkey::Pubkey,
15 spl_pod::{
16 bytemuck::pod_get_packed_len,
17 optional_keys::OptionalNonZeroPubkey,
18 primitives::{PodBool, PodU64},
19 },
20};
21
22#[repr(C)]
24#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
25pub struct PodMint {
26 pub mint_authority: PodCOption<Pubkey>,
31 pub supply: PodU64,
33 pub decimals: u8,
35 pub is_initialized: PodBool,
37 pub freeze_authority: PodCOption<Pubkey>,
39}
40impl IsInitialized for PodMint {
41 fn is_initialized(&self) -> bool {
42 self.is_initialized.into()
43 }
44}
45impl PackedSizeOf for PodMint {
46 const SIZE_OF: usize = pod_get_packed_len::<Self>();
47}
48#[cfg(test)]
49impl From<Mint> for PodMint {
50 fn from(mint: Mint) -> Self {
51 Self {
52 mint_authority: mint.mint_authority.into(),
53 supply: mint.supply.into(),
54 decimals: mint.decimals,
55 is_initialized: mint.is_initialized.into(),
56 freeze_authority: mint.freeze_authority.into(),
57 }
58 }
59}
60
61#[repr(C)]
63#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
64pub struct PodAccount {
65 pub mint: Pubkey,
67 pub owner: Pubkey,
69 pub amount: PodU64,
71 pub delegate: PodCOption<Pubkey>,
74 pub state: u8,
76 pub is_native: PodCOption<PodU64>,
81 pub delegated_amount: PodU64,
83 pub close_authority: PodCOption<Pubkey>,
85}
86impl PodAccount {
87 pub fn is_frozen(&self) -> bool {
89 self.state == AccountState::Frozen as u8
90 }
91 pub fn is_native(&self) -> bool {
93 self.is_native.is_some()
94 }
95 pub fn is_owned_by_system_program_or_incinerator(&self) -> bool {
98 solana_sdk_ids::system_program::check_id(&self.owner)
99 || solana_sdk_ids::incinerator::check_id(&self.owner)
100 }
101}
102impl IsInitialized for PodAccount {
103 fn is_initialized(&self) -> bool {
104 self.state == AccountState::Initialized as u8 || self.state == AccountState::Frozen as u8
105 }
106}
107impl PackedSizeOf for PodAccount {
108 const SIZE_OF: usize = pod_get_packed_len::<Self>();
109}
110#[cfg(test)]
111impl From<Account> for PodAccount {
112 fn from(account: Account) -> Self {
113 Self {
114 mint: account.mint,
115 owner: account.owner,
116 amount: account.amount.into(),
117 delegate: account.delegate.into(),
118 state: account.state.into(),
119 is_native: account.is_native.map(PodU64::from_primitive).into(),
120 delegated_amount: account.delegated_amount.into(),
121 close_authority: account.close_authority.into(),
122 }
123 }
124}
125
126#[repr(C)]
128#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
129pub struct PodMultisig {
130 pub m: u8,
132 pub n: u8,
134 pub is_initialized: PodBool,
136 pub signers: [Pubkey; MAX_SIGNERS],
138}
139impl IsInitialized for PodMultisig {
140 fn is_initialized(&self) -> bool {
141 self.is_initialized.into()
142 }
143}
144impl PackedSizeOf for PodMultisig {
145 const SIZE_OF: usize = pod_get_packed_len::<Self>();
146}
147#[cfg(test)]
148impl From<Multisig> for PodMultisig {
149 fn from(multisig: Multisig) -> Self {
150 Self {
151 m: multisig.m,
152 n: multisig.n,
153 is_initialized: multisig.is_initialized.into(),
154 signers: multisig.signers,
155 }
156 }
157}
158
159#[repr(C, packed)]
161#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
162pub struct PodCOption<T>
163where
164 T: Pod + Default,
165{
166 pub option: [u8; 4],
168 pub value: T,
170}
171impl<T> PodCOption<T>
172where
173 T: Pod + Default,
174{
175 pub const NONE: [u8; 4] = [0; 4];
177 pub const SOME: [u8; 4] = [1, 0, 0, 0];
180
181 pub fn none() -> Self {
186 Self {
187 option: Self::NONE,
188 value: T::default(),
189 }
190 }
191
192 pub const fn some(value: T) -> Self {
194 Self {
195 option: Self::SOME,
196 value,
197 }
198 }
199
200 pub fn unwrap_or(self, default: T) -> T {
203 if self.option == Self::NONE {
204 default
205 } else {
206 self.value
207 }
208 }
209
210 pub fn is_some(&self) -> bool {
212 self.option == Self::SOME
213 }
214
215 pub fn is_none(&self) -> bool {
217 self.option == Self::NONE
218 }
219
220 pub fn ok_or<E>(self, error: E) -> Result<T, E> {
222 match self {
223 Self {
224 option: Self::SOME,
225 value,
226 } => Ok(value),
227 _ => Err(error),
228 }
229 }
230}
231impl<T: Pod + Default> From<COption<T>> for PodCOption<T> {
232 fn from(opt: COption<T>) -> Self {
233 match opt {
234 COption::None => Self {
235 option: Self::NONE,
236 value: T::default(),
237 },
238 COption::Some(v) => Self {
239 option: Self::SOME,
240 value: v,
241 },
242 }
243 }
244}
245impl TryFrom<PodCOption<Pubkey>> for OptionalNonZeroPubkey {
246 type Error = ProgramError;
247 fn try_from(p: PodCOption<Pubkey>) -> Result<Self, Self::Error> {
248 match p {
249 PodCOption {
250 option: PodCOption::<Pubkey>::SOME,
251 value,
252 } if value == Pubkey::default() => Err(ProgramError::InvalidArgument),
253 PodCOption {
254 option: PodCOption::<Pubkey>::SOME,
255 value,
256 } => Ok(Self(value)),
257 PodCOption {
258 option: PodCOption::<Pubkey>::NONE,
259 value: _,
260 } => Ok(Self(Pubkey::default())),
261 _ => unreachable!(),
262 }
263 }
264}
265
266#[cfg(test)]
267pub(crate) mod test {
268 use {
269 super::*,
270 crate::state::{
271 test::{
272 TEST_ACCOUNT, TEST_ACCOUNT_SLICE, TEST_MINT, TEST_MINT_SLICE, TEST_MULTISIG,
273 TEST_MULTISIG_SLICE,
274 },
275 AccountState,
276 },
277 spl_pod::bytemuck::pod_from_bytes,
278 };
279
280 pub const TEST_POD_MINT: PodMint = PodMint {
281 mint_authority: PodCOption::some(Pubkey::new_from_array([1; 32])),
282 supply: PodU64::from_primitive(42),
283 decimals: 7,
284 is_initialized: PodBool::from_bool(true),
285 freeze_authority: PodCOption::some(Pubkey::new_from_array([2; 32])),
286 };
287 pub const TEST_POD_ACCOUNT: PodAccount = PodAccount {
288 mint: Pubkey::new_from_array([1; 32]),
289 owner: Pubkey::new_from_array([2; 32]),
290 amount: PodU64::from_primitive(3),
291 delegate: PodCOption::some(Pubkey::new_from_array([4; 32])),
292 state: AccountState::Frozen as u8,
293 is_native: PodCOption::some(PodU64::from_primitive(5)),
294 delegated_amount: PodU64::from_primitive(6),
295 close_authority: PodCOption::some(Pubkey::new_from_array([7; 32])),
296 };
297
298 #[test]
299 fn pod_mint_to_mint_equality() {
300 let pod_mint = pod_from_bytes::<PodMint>(TEST_MINT_SLICE).unwrap();
301 assert_eq!(*pod_mint, PodMint::from(TEST_MINT));
302 assert_eq!(*pod_mint, TEST_POD_MINT);
303 }
304
305 #[test]
306 fn pod_account_to_account_equality() {
307 let pod_account = pod_from_bytes::<PodAccount>(TEST_ACCOUNT_SLICE).unwrap();
308 assert_eq!(*pod_account, PodAccount::from(TEST_ACCOUNT));
309 assert_eq!(*pod_account, TEST_POD_ACCOUNT);
310 }
311
312 #[test]
313 fn pod_multisig_to_multisig_equality() {
314 let pod_multisig = pod_from_bytes::<PodMultisig>(TEST_MULTISIG_SLICE).unwrap();
315 assert_eq!(*pod_multisig, PodMultisig::from(TEST_MULTISIG));
316 }
317}