1#[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::{
12 program_error::ProgramError, program_option::COption, program_pack::IsInitialized,
13 pubkey::Pubkey,
14 },
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_program::system_program::check_id(&self.owner)
99 || solana_program::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(crate) option: [u8; 4],
167 pub(crate) value: T,
168}
169impl<T> PodCOption<T>
170where
171 T: Pod + Default,
172{
173 pub const NONE: [u8; 4] = [0; 4];
175 pub const SOME: [u8; 4] = [1, 0, 0, 0];
178
179 pub fn none() -> Self {
184 Self {
185 option: Self::NONE,
186 value: T::default(),
187 }
188 }
189
190 pub const fn some(value: T) -> Self {
192 Self {
193 option: Self::SOME,
194 value,
195 }
196 }
197
198 pub fn unwrap_or(self, default: T) -> T {
201 if self.option == Self::NONE {
202 default
203 } else {
204 self.value
205 }
206 }
207
208 pub fn is_some(&self) -> bool {
210 self.option == Self::SOME
211 }
212
213 pub fn is_none(&self) -> bool {
215 self.option == Self::NONE
216 }
217
218 pub fn ok_or<E>(self, error: E) -> Result<T, E> {
220 match self {
221 Self {
222 option: Self::SOME,
223 value,
224 } => Ok(value),
225 _ => Err(error),
226 }
227 }
228}
229impl<T: Pod + Default> From<COption<T>> for PodCOption<T> {
230 fn from(opt: COption<T>) -> Self {
231 match opt {
232 COption::None => Self {
233 option: Self::NONE,
234 value: T::default(),
235 },
236 COption::Some(v) => Self {
237 option: Self::SOME,
238 value: v,
239 },
240 }
241 }
242}
243impl TryFrom<PodCOption<Pubkey>> for OptionalNonZeroPubkey {
244 type Error = ProgramError;
245 fn try_from(p: PodCOption<Pubkey>) -> Result<Self, Self::Error> {
246 match p {
247 PodCOption {
248 option: PodCOption::<Pubkey>::SOME,
249 value,
250 } if value == Pubkey::default() => Err(ProgramError::InvalidArgument),
251 PodCOption {
252 option: PodCOption::<Pubkey>::SOME,
253 value,
254 } => Ok(Self(value)),
255 PodCOption {
256 option: PodCOption::<Pubkey>::NONE,
257 value: _,
258 } => Ok(Self(Pubkey::default())),
259 _ => unreachable!(),
260 }
261 }
262}
263
264#[cfg(test)]
265pub(crate) mod test {
266 use {
267 super::*,
268 crate::state::{
269 test::{
270 TEST_ACCOUNT, TEST_ACCOUNT_SLICE, TEST_MINT, TEST_MINT_SLICE, TEST_MULTISIG,
271 TEST_MULTISIG_SLICE,
272 },
273 AccountState,
274 },
275 spl_pod::bytemuck::pod_from_bytes,
276 };
277
278 pub const TEST_POD_MINT: PodMint = PodMint {
279 mint_authority: PodCOption::some(Pubkey::new_from_array([1; 32])),
280 supply: PodU64::from_primitive(42),
281 decimals: 7,
282 is_initialized: PodBool::from_bool(true),
283 freeze_authority: PodCOption::some(Pubkey::new_from_array([2; 32])),
284 };
285 pub const TEST_POD_ACCOUNT: PodAccount = PodAccount {
286 mint: Pubkey::new_from_array([1; 32]),
287 owner: Pubkey::new_from_array([2; 32]),
288 amount: PodU64::from_primitive(3),
289 delegate: PodCOption::some(Pubkey::new_from_array([4; 32])),
290 state: AccountState::Frozen as u8,
291 is_native: PodCOption::some(PodU64::from_primitive(5)),
292 delegated_amount: PodU64::from_primitive(6),
293 close_authority: PodCOption::some(Pubkey::new_from_array([7; 32])),
294 };
295
296 #[test]
297 fn pod_mint_to_mint_equality() {
298 let pod_mint = pod_from_bytes::<PodMint>(TEST_MINT_SLICE).unwrap();
299 assert_eq!(*pod_mint, PodMint::from(TEST_MINT));
300 assert_eq!(*pod_mint, TEST_POD_MINT);
301 }
302
303 #[test]
304 fn pod_account_to_account_equality() {
305 let pod_account = pod_from_bytes::<PodAccount>(TEST_ACCOUNT_SLICE).unwrap();
306 assert_eq!(*pod_account, PodAccount::from(TEST_ACCOUNT));
307 assert_eq!(*pod_account, TEST_POD_ACCOUNT);
308 }
309
310 #[test]
311 fn pod_multisig_to_multisig_equality() {
312 let pod_multisig = pod_from_bytes::<PodMultisig>(TEST_MULTISIG_SLICE).unwrap();
313 assert_eq!(*pod_multisig, PodMultisig::from(TEST_MULTISIG));
314 }
315}