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