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_address::Address,
12 solana_nullable::MaybeNull,
13 solana_program_error::ProgramError,
14 solana_program_option::COption,
15 solana_program_pack::IsInitialized,
16 solana_zero_copy::unaligned::{Bool, U64},
17};
18
19#[repr(C)]
21#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
22pub struct PodMint {
23 pub mint_authority: PodCOption<Address>,
28 pub supply: U64,
30 pub decimals: u8,
32 pub is_initialized: Bool,
34 pub freeze_authority: PodCOption<Address>,
36}
37impl IsInitialized for PodMint {
38 fn is_initialized(&self) -> bool {
39 self.is_initialized.into()
40 }
41}
42impl PackedSizeOf for PodMint {
43 const SIZE_OF: usize = size_of::<Self>();
44}
45#[cfg(test)]
46impl From<Mint> for PodMint {
47 fn from(mint: Mint) -> Self {
48 Self {
49 mint_authority: mint.mint_authority.into(),
50 supply: mint.supply.into(),
51 decimals: mint.decimals,
52 is_initialized: mint.is_initialized.into(),
53 freeze_authority: mint.freeze_authority.into(),
54 }
55 }
56}
57
58#[repr(C)]
60#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
61pub struct PodAccount {
62 pub mint: Address,
64 pub owner: Address,
66 pub amount: U64,
68 pub delegate: PodCOption<Address>,
71 pub state: u8,
73 pub is_native: PodCOption<U64>,
78 pub delegated_amount: U64,
80 pub close_authority: PodCOption<Address>,
82}
83impl PodAccount {
84 pub fn is_frozen(&self) -> bool {
86 self.state == AccountState::Frozen as u8
87 }
88 pub fn is_native(&self) -> bool {
90 self.is_native.is_some()
91 }
92 pub fn is_owned_by_system_program_or_incinerator(&self) -> bool {
95 solana_sdk_ids::system_program::check_id(&self.owner)
96 || solana_sdk_ids::incinerator::check_id(&self.owner)
97 }
98}
99impl IsInitialized for PodAccount {
100 fn is_initialized(&self) -> bool {
101 self.state == AccountState::Initialized as u8 || self.state == AccountState::Frozen as u8
102 }
103}
104impl PackedSizeOf for PodAccount {
105 const SIZE_OF: usize = size_of::<Self>();
106}
107#[cfg(test)]
108impl From<Account> for PodAccount {
109 fn from(account: Account) -> Self {
110 Self {
111 mint: account.mint,
112 owner: account.owner,
113 amount: account.amount.into(),
114 delegate: account.delegate.into(),
115 state: account.state.into(),
116 is_native: account.is_native.map(U64::from_primitive).into(),
117 delegated_amount: account.delegated_amount.into(),
118 close_authority: account.close_authority.into(),
119 }
120 }
121}
122
123#[repr(C)]
125#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
126pub struct PodMultisig {
127 pub m: u8,
129 pub n: u8,
131 pub is_initialized: Bool,
133 pub signers: [Address; MAX_SIGNERS],
135}
136impl IsInitialized for PodMultisig {
137 fn is_initialized(&self) -> bool {
138 self.is_initialized.into()
139 }
140}
141impl PackedSizeOf for PodMultisig {
142 const SIZE_OF: usize = size_of::<Self>();
143}
144#[cfg(test)]
145impl From<Multisig> for PodMultisig {
146 fn from(multisig: Multisig) -> Self {
147 Self {
148 m: multisig.m,
149 n: multisig.n,
150 is_initialized: multisig.is_initialized.into(),
151 signers: multisig.signers,
152 }
153 }
154}
155
156#[repr(C, packed)]
158#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
159pub struct PodCOption<T>
160where
161 T: Pod + Default,
162{
163 pub option: [u8; 4],
165 pub value: T,
167}
168impl<T> PodCOption<T>
169where
170 T: Pod + Default,
171{
172 pub const NONE: [u8; 4] = [0; 4];
174 pub const SOME: [u8; 4] = [1, 0, 0, 0];
177
178 pub fn none() -> Self {
183 Self {
184 option: Self::NONE,
185 value: T::default(),
186 }
187 }
188
189 pub const fn some(value: T) -> Self {
191 Self {
192 option: Self::SOME,
193 value,
194 }
195 }
196
197 pub fn unwrap_or(self, default: T) -> T {
200 if self.option == Self::NONE {
201 default
202 } else {
203 self.value
204 }
205 }
206
207 pub fn is_some(&self) -> bool {
209 self.option == Self::SOME
210 }
211
212 pub fn is_none(&self) -> bool {
214 self.option == Self::NONE
215 }
216
217 pub fn ok_or<E>(self, error: E) -> Result<T, E> {
219 match self {
220 Self {
221 option: Self::SOME,
222 value,
223 } => Ok(value),
224 _ => Err(error),
225 }
226 }
227}
228impl<T: Pod + Default> From<COption<T>> for PodCOption<T> {
229 fn from(opt: COption<T>) -> Self {
230 match opt {
231 COption::None => Self {
232 option: Self::NONE,
233 value: T::default(),
234 },
235 COption::Some(v) => Self {
236 option: Self::SOME,
237 value: v,
238 },
239 }
240 }
241}
242impl TryFrom<PodCOption<Address>> for MaybeNull<Address> {
243 type Error = ProgramError;
244 fn try_from(p: PodCOption<Address>) -> Result<Self, Self::Error> {
245 match p {
246 PodCOption {
247 option: PodCOption::<Address>::SOME,
248 value,
249 } if value == Address::default() => Err(ProgramError::InvalidArgument),
250 PodCOption {
251 option: PodCOption::<Address>::SOME,
252 value,
253 } => Ok(Self::from(value)),
254 PodCOption {
255 option: PodCOption::<Address>::NONE,
256 value: _,
257 } => Ok(Self::default()),
258 _ => unreachable!(),
259 }
260 }
261}
262
263#[cfg(test)]
264pub(crate) mod test {
265 use {
266 super::*,
267 crate::state::{
268 test::{
269 TEST_ACCOUNT, TEST_ACCOUNT_SLICE, TEST_MINT, TEST_MINT_SLICE, TEST_MULTISIG,
270 TEST_MULTISIG_SLICE,
271 },
272 AccountState,
273 },
274 };
275
276 pub const TEST_POD_MINT: PodMint = PodMint {
277 mint_authority: PodCOption::some(Address::new_from_array([1; 32])),
278 supply: U64::from_primitive(42),
279 decimals: 7,
280 is_initialized: Bool::from_bool(true),
281 freeze_authority: PodCOption::some(Address::new_from_array([2; 32])),
282 };
283 pub const TEST_POD_ACCOUNT: PodAccount = PodAccount {
284 mint: Address::new_from_array([1; 32]),
285 owner: Address::new_from_array([2; 32]),
286 amount: U64::from_primitive(3),
287 delegate: PodCOption::some(Address::new_from_array([4; 32])),
288 state: AccountState::Frozen as u8,
289 is_native: PodCOption::some(U64::from_primitive(5)),
290 delegated_amount: U64::from_primitive(6),
291 close_authority: PodCOption::some(Address::new_from_array([7; 32])),
292 };
293
294 #[test]
295 fn pod_mint_to_mint_equality() {
296 let pod_mint = bytemuck::try_from_bytes::<PodMint>(TEST_MINT_SLICE).unwrap();
297 assert_eq!(*pod_mint, PodMint::from(TEST_MINT));
298 assert_eq!(*pod_mint, TEST_POD_MINT);
299 }
300
301 #[test]
302 fn pod_account_to_account_equality() {
303 let pod_account = bytemuck::try_from_bytes::<PodAccount>(TEST_ACCOUNT_SLICE).unwrap();
304 assert_eq!(*pod_account, PodAccount::from(TEST_ACCOUNT));
305 assert_eq!(*pod_account, TEST_POD_ACCOUNT);
306 }
307
308 #[test]
309 fn pod_multisig_to_multisig_equality() {
310 let pod_multisig = bytemuck::try_from_bytes::<PodMultisig>(TEST_MULTISIG_SLICE).unwrap();
311 assert_eq!(*pod_multisig, PodMultisig::from(TEST_MULTISIG));
312 }
313}