light_token_interface/state/mint/
zero_copy.rs1use core::ops::Deref;
2
3use aligned_sized::aligned_sized;
4use light_compressed_account::Pubkey;
5use light_compressible::compression_info::CompressionInfo;
6use light_program_profiler::profile;
7use light_zero_copy::{
8 traits::{ZeroCopyAt, ZeroCopyAtMut},
9 ZeroCopy, ZeroCopyMut, ZeroCopyNew,
10};
11use spl_pod::solana_msg::msg;
12
13use super::compressed_mint::{MintMetadata, ACCOUNT_TYPE_MINT};
14use crate::{
15 instructions::mint_action::MintInstructionData,
16 state::{
17 ExtensionStruct, ExtensionStructConfig, Mint, TokenDataVersion, ZExtensionStruct,
18 ZExtensionStructMut,
19 },
20 AnchorDeserialize, AnchorSerialize, TokenError,
21};
22
23pub const BASE_MINT_ACCOUNT_SIZE: u64 = MintZeroCopyMeta::LEN as u64;
25
26#[derive(
29 Debug, PartialEq, Eq, Clone, AnchorSerialize, AnchorDeserialize, ZeroCopy, ZeroCopyMut,
30)]
31#[repr(C)]
32#[aligned_sized]
33struct MintZeroCopyMeta {
34 mint_authority_option_prefix: u32,
36 mint_authority: Pubkey,
37 pub supply: u64,
39 pub decimals: u8,
41 pub is_initialized: u8,
43 freeze_authority_option_prefix: u32,
44 freeze_authority: Pubkey,
45 pub metadata: MintMetadata,
47 pub reserved: [u8; 16],
49 pub account_type: u8,
51 pub compression: CompressionInfo,
53 has_extensions: bool,
55}
56
57#[derive(Debug)]
59pub struct ZMint<'a> {
60 pub base: ZMintZeroCopyMeta<'a>,
61 pub extensions: Option<Vec<ZExtensionStruct<'a>>>,
62}
63
64#[derive(Debug)]
66pub struct ZMintMut<'a> {
67 pub base: ZMintZeroCopyMetaMut<'a>,
68 pub extensions: Option<Vec<ZExtensionStructMut<'a>>>,
69}
70
71#[derive(Debug, Clone, PartialEq)]
73pub struct MintConfig {
74 pub extensions: Option<Vec<ExtensionStructConfig>>,
76}
77
78impl<'a> ZeroCopyNew<'a> for Mint {
79 type ZeroCopyConfig = MintConfig;
80 type Output = ZMintMut<'a>;
81
82 fn byte_len(
83 config: &Self::ZeroCopyConfig,
84 ) -> Result<usize, light_zero_copy::errors::ZeroCopyError> {
85 let meta_config = MintZeroCopyMetaConfig {
87 metadata: (),
88 compression: light_compressible::compression_info::CompressionInfoConfig {
89 rent_config: (),
90 },
91 };
92 let mut size = MintZeroCopyMeta::byte_len(&meta_config)?;
93
94 if let Some(ref extensions) = config.extensions {
96 size += 4;
98 for ext_config in extensions {
99 size += ExtensionStruct::byte_len(ext_config)?;
100 }
101 }
102
103 Ok(size)
104 }
105
106 fn new_zero_copy(
107 bytes: &'a mut [u8],
108 config: Self::ZeroCopyConfig,
109 ) -> Result<(Self::Output, &'a mut [u8]), light_zero_copy::errors::ZeroCopyError> {
110 const IS_INITIALIZED_OFFSET: usize = 45; if bytes.len() > IS_INITIALIZED_OFFSET && bytes[IS_INITIALIZED_OFFSET] != 0 {
113 return Err(light_zero_copy::errors::ZeroCopyError::MemoryNotZeroed);
114 }
115 let meta_config = MintZeroCopyMetaConfig {
117 metadata: (),
118 compression: light_compressible::compression_info::CompressionInfoConfig {
119 rent_config: (),
120 },
121 };
122 let (mut base, remaining) =
123 <MintZeroCopyMeta as ZeroCopyNew<'a>>::new_zero_copy(bytes, meta_config)?;
124 *base.account_type = ACCOUNT_TYPE_MINT;
125 base.is_initialized = 1;
126
127 if let Some(extensions_config) = config.extensions {
129 *base.has_extensions = 1u8;
130 let (extensions, remaining) = <Vec<ExtensionStruct> as ZeroCopyNew<'a>>::new_zero_copy(
131 remaining,
132 extensions_config,
133 )?;
134
135 Ok((
136 ZMintMut {
137 base,
138 extensions: Some(extensions),
139 },
140 remaining,
141 ))
142 } else {
143 Ok((
144 ZMintMut {
145 base,
146 extensions: None,
147 },
148 remaining,
149 ))
150 }
151 }
152}
153
154impl<'a> ZeroCopyAt<'a> for Mint {
155 type ZeroCopyAt = ZMint<'a>;
156
157 fn zero_copy_at(
158 bytes: &'a [u8],
159 ) -> Result<(Self::ZeroCopyAt, &'a [u8]), light_zero_copy::errors::ZeroCopyError> {
160 let (base, bytes) = <MintZeroCopyMeta as ZeroCopyAt<'a>>::zero_copy_at(bytes)?;
161 if base.has_extensions() {
163 let (extensions, bytes) =
164 <Vec<ExtensionStruct> as ZeroCopyAt<'a>>::zero_copy_at(bytes)?;
165 Ok((
166 ZMint {
167 base,
168 extensions: Some(extensions),
169 },
170 bytes,
171 ))
172 } else {
173 Ok((
174 ZMint {
175 base,
176 extensions: None,
177 },
178 bytes,
179 ))
180 }
181 }
182}
183
184impl<'a> ZeroCopyAtMut<'a> for Mint {
185 type ZeroCopyAtMut = ZMintMut<'a>;
186
187 fn zero_copy_at_mut(
188 bytes: &'a mut [u8],
189 ) -> Result<(Self::ZeroCopyAtMut, &'a mut [u8]), light_zero_copy::errors::ZeroCopyError> {
190 let (base, bytes) = <MintZeroCopyMeta as ZeroCopyAtMut<'a>>::zero_copy_at_mut(bytes)?;
191 if base.has_extensions() {
193 let (extensions, bytes) =
194 <Vec<ExtensionStruct> as ZeroCopyAtMut<'a>>::zero_copy_at_mut(bytes)?;
195 Ok((
196 ZMintMut {
197 base,
198 extensions: Some(extensions),
199 },
200 bytes,
201 ))
202 } else {
203 Ok((
204 ZMintMut {
205 base,
206 extensions: None,
207 },
208 bytes,
209 ))
210 }
211 }
212}
213
214impl<'a> Deref for ZMint<'a> {
216 type Target = ZMintZeroCopyMeta<'a>;
217
218 fn deref(&self) -> &Self::Target {
219 &self.base
220 }
221}
222
223impl<'a> Deref for ZMintMut<'a> {
224 type Target = ZMintZeroCopyMetaMut<'a>;
225
226 fn deref(&self) -> &Self::Target {
227 &self.base
228 }
229}
230
231impl ZMintZeroCopyMeta<'_> {
233 #[inline(always)]
235 pub fn is_mint_account(&self) -> bool {
236 self.account_type == ACCOUNT_TYPE_MINT
237 }
238
239 #[inline(always)]
241 pub fn is_initialized(&self) -> bool {
242 self.is_initialized != 0
243 }
244
245 pub fn mint_authority(&self) -> Option<&Pubkey> {
247 if u32::from(self.mint_authority_option_prefix) == 1 {
248 Some(&self.mint_authority)
249 } else {
250 None
251 }
252 }
253
254 pub fn freeze_authority(&self) -> Option<&Pubkey> {
256 if u32::from(self.freeze_authority_option_prefix) == 1 {
257 Some(&self.freeze_authority)
258 } else {
259 None
260 }
261 }
262}
263
264impl ZMintZeroCopyMetaMut<'_> {
266 #[inline(always)]
268 pub fn is_mint_account(&self) -> bool {
269 *self.account_type == ACCOUNT_TYPE_MINT
270 }
271
272 #[inline(always)]
274 pub fn is_initialized(&self) -> bool {
275 self.is_initialized == 1
276 }
277
278 pub fn mint_authority(&self) -> Option<&Pubkey> {
280 if u32::from(self.mint_authority_option_prefix) == 1 {
281 Some(&self.mint_authority)
282 } else {
283 None
284 }
285 }
286
287 pub fn set_mint_authority(&mut self, pubkey: Option<Pubkey>) {
289 if let Some(pubkey) = pubkey {
290 self.mint_authority_option_prefix = 1u32.into();
291 self.mint_authority = pubkey;
292 } else {
293 self.mint_authority_option_prefix = 0u32.into();
294 self.mint_authority = Pubkey::default();
295 }
296 }
297
298 pub fn freeze_authority(&self) -> Option<&Pubkey> {
300 if u32::from(self.freeze_authority_option_prefix) == 1 {
301 Some(&self.freeze_authority)
302 } else {
303 None
304 }
305 }
306
307 pub fn set_freeze_authority(&mut self, pubkey: Option<Pubkey>) {
309 if let Some(pubkey) = pubkey {
310 self.freeze_authority_option_prefix = 1u32.into();
311 self.freeze_authority = pubkey;
312 } else {
313 self.freeze_authority_option_prefix = 0u32.into();
314 self.freeze_authority = Pubkey::default();
315 }
316 }
317}
318
319impl Mint {
321 #[profile]
326 pub fn zero_copy_at_checked(bytes: &[u8]) -> Result<(ZMint<'_>, &[u8]), TokenError> {
327 if bytes.len() < BASE_MINT_ACCOUNT_SIZE as usize {
329 return Err(TokenError::InvalidAccountData);
330 }
331
332 let (mint, remaining) =
334 Mint::zero_copy_at(bytes).map_err(|_| TokenError::MintDeserializationFailed)?;
335
336 if !mint.is_mint_account() {
338 return Err(TokenError::InvalidAccountType);
339 }
340
341 if !mint.is_initialized() {
343 return Err(TokenError::MintNotInitialized);
344 }
345
346 Ok((mint, remaining))
347 }
348
349 #[profile]
354 pub fn zero_copy_at_mut_checked(
355 bytes: &mut [u8],
356 ) -> Result<(ZMintMut<'_>, &mut [u8]), TokenError> {
357 if bytes.len() < BASE_MINT_ACCOUNT_SIZE as usize {
359 msg!(
360 "zero_copy_at_mut_checked bytes.len() < BASE_MINT_ACCOUNT_SIZE {}",
361 bytes.len()
362 );
363 return Err(TokenError::InvalidAccountData);
364 }
365
366 let (mint, remaining) =
367 Mint::zero_copy_at_mut(bytes).map_err(|_| TokenError::MintDeserializationFailed)?;
368
369 if !mint.is_initialized() {
370 return Err(TokenError::MintNotInitialized);
371 }
372 if !mint.is_mint_account() {
373 return Err(TokenError::InvalidAccountType);
374 }
375
376 Ok((mint, remaining))
377 }
378}
379
380impl ZMint<'_> {
382 #[inline(always)]
384 pub fn is_mint_account(&self) -> bool {
385 self.base.is_mint_account()
386 }
387
388 #[inline(always)]
390 pub fn is_initialized(&self) -> bool {
391 self.base.is_initialized()
392 }
393}
394
395impl ZMintMut<'_> {
397 #[inline(always)]
399 pub fn is_mint_account(&self) -> bool {
400 self.base.is_mint_account()
401 }
402
403 #[inline(always)]
405 pub fn is_initialized(&self) -> bool {
406 self.base.is_initialized()
407 }
408
409 #[inline]
411 #[profile]
412 pub fn set(
413 &mut self,
414 ix_data: &<MintInstructionData as light_zero_copy::traits::ZeroCopyAt<'_>>::ZeroCopyAt,
415 mint_decompressed: bool,
416 ) -> Result<(), TokenError> {
417 if ix_data.metadata.version != TokenDataVersion::ShaFlat as u8 {
418 #[cfg(feature = "solana")]
419 msg!(
420 "Only shaflat version 3 is supported got {}",
421 ix_data.metadata.version
422 );
423 return Err(TokenError::InvalidTokenMetadataVersion);
424 }
425 self.base.metadata.version = ix_data.metadata.version;
427 self.base.metadata.mint = ix_data.metadata.mint;
428 self.base.metadata.mint_decompressed = if mint_decompressed { 1 } else { 0 };
429 self.base.metadata.mint_signer = ix_data.metadata.mint_signer;
430 self.base.metadata.bump = ix_data.metadata.bump;
431
432 self.base.supply = ix_data.supply;
434 self.base.decimals = ix_data.decimals;
435 self.base.is_initialized = 1; if let Some(mint_authority) = ix_data.mint_authority.as_deref() {
438 self.base.set_mint_authority(Some(*mint_authority));
439 }
440 if let Some(freeze_authority) = ix_data.freeze_authority.as_deref() {
442 self.base.set_freeze_authority(Some(*freeze_authority));
443 }
444
445 Ok(())
448 }
449}