light_token_interface/instructions/mint_action/
instruction_data.rs

1use light_compressed_account::{instruction_data::compressed_proof::CompressedProof, Pubkey};
2use light_compressible::compression_info::CompressionInfo;
3use light_zero_copy::ZeroCopy;
4
5use super::{
6    CompressAndCloseMintAction, CpiContext, DecompressMintAction, MintToAction,
7    MintToCompressedAction, RemoveMetadataKeyAction, UpdateAuthority,
8    UpdateMetadataAuthorityAction, UpdateMetadataFieldAction,
9};
10use crate::{
11    instructions::extensions::{ExtensionInstructionData, ZExtensionInstructionData},
12    state::{AdditionalMetadata, BaseMint, ExtensionStruct, Mint, MintMetadata, TokenMetadata},
13    AnchorDeserialize, AnchorSerialize, TokenError,
14};
15
16#[repr(C)]
17#[derive(Debug, Clone, AnchorSerialize, AnchorDeserialize, ZeroCopy)]
18pub enum Action {
19    /// Mint compressed tokens to compressed accounts.
20    MintToCompressed(MintToCompressedAction),
21    /// Update mint authority of a compressed mint account.
22    UpdateMintAuthority(UpdateAuthority),
23    /// Update freeze authority of a compressed mint account.
24    UpdateFreezeAuthority(UpdateAuthority),
25    /// Mint tokens from a compressed mint to a token solana account
26    /// (tokens are not compressed but not spl tokens).
27    MintTo(MintToAction),
28    UpdateMetadataField(UpdateMetadataFieldAction),
29    UpdateMetadataAuthority(UpdateMetadataAuthorityAction),
30    RemoveMetadataKey(RemoveMetadataKeyAction),
31    /// Decompress a compressed mint to a Mint Solana account.
32    /// Creates a Mint PDA that becomes the source of truth.
33    DecompressMint(DecompressMintAction),
34    /// Compress and close a Mint Solana account. The compressed mint state is preserved.
35    /// Permissionless - anyone can call if is_compressible() returns true (rent expired).
36    CompressAndCloseMint(CompressAndCloseMintAction),
37}
38
39#[repr(C)]
40#[derive(Debug, Clone, AnchorSerialize, AnchorDeserialize, ZeroCopy)]
41pub struct MintActionCompressedInstructionData {
42    /// Only set if mint already exists
43    pub leaf_index: u32,
44    /// Only set if mint already exists
45    pub prove_by_index: bool,
46    /// If create mint, root index of address proof
47    /// If mint already exists, root index of validity proof
48    /// If proof by index not used.
49    pub root_index: u16,
50    /// Maximum lamports for rent and top-up combined. Transaction fails if exceeded. (0 = no limit)
51    pub max_top_up: u16,
52    pub create_mint: Option<CreateMint>,
53    pub actions: Vec<Action>,
54    pub proof: Option<CompressedProof>,
55    pub cpi_context: Option<CpiContext>,
56    pub mint: Option<MintInstructionData>,
57}
58
59#[repr(C)]
60#[derive(Debug, Clone, AnchorSerialize, Default, AnchorDeserialize, ZeroCopy)]
61pub struct CreateMint {
62    /// Placeholder to enable mints in multiple address trees.
63    /// Currently set to 0.
64    pub read_only_address_trees: [u8; 4],
65    /// Placeholder to enable mints in multiple address trees.
66    /// Currently set to 0.
67    pub read_only_address_tree_root_indices: [u16; 4],
68}
69
70#[repr(C)]
71#[derive(Debug, Clone, AnchorSerialize, AnchorDeserialize, ZeroCopy, PartialEq)]
72pub struct MintWithContext {
73    pub leaf_index: u32,
74    pub prove_by_index: bool,
75    pub root_index: u16,
76    pub address: [u8; 32],
77    pub mint: Option<MintInstructionData>,
78}
79
80#[repr(C)]
81#[derive(Debug, PartialEq, Eq, Clone, AnchorSerialize, AnchorDeserialize, ZeroCopy)]
82pub struct MintInstructionData {
83    /// Total supply of tokens.
84    pub supply: u64,
85    /// Number of base 10 digits to the right of the decimal place.
86    pub decimals: u8,
87    /// Light Protocol-specific metadata
88    pub metadata: MintMetadata,
89    /// Optional authority used to mint new tokens. The mint authority may only
90    /// be provided during mint creation. If no mint authority is present
91    /// then the mint has a fixed supply and no further tokens may be
92    /// minted.
93    pub mint_authority: Option<Pubkey>,
94    /// Optional authority to freeze token accounts.
95    pub freeze_authority: Option<Pubkey>,
96    /// Extensions for additional functionality
97    pub extensions: Option<Vec<ExtensionInstructionData>>,
98}
99
100impl TryFrom<Mint> for MintInstructionData {
101    type Error = TokenError;
102
103    fn try_from(mint: Mint) -> Result<Self, Self::Error> {
104        let extensions = match mint.extensions {
105            Some(exts) if !exts.is_empty() => {
106                let mut extension_list = Vec::with_capacity(exts.len());
107                for ext in exts {
108                    match ext {
109                        ExtensionStruct::TokenMetadata(token_metadata) => {
110                            extension_list.push(ExtensionInstructionData::TokenMetadata(
111                                crate::instructions::extensions::token_metadata::TokenMetadataInstructionData {
112                                    update_authority: if token_metadata.update_authority == [0u8;32] {None}else {Some(token_metadata.update_authority)},
113                                    name: token_metadata.name,
114                                    symbol: token_metadata.symbol,
115                                    uri: token_metadata.uri,
116                                    additional_metadata: Some(token_metadata.additional_metadata),
117                                },
118                            ));
119                        }
120                        _ => {
121                            return Err(TokenError::UnsupportedExtension);
122                        }
123                    }
124                }
125                Some(extension_list)
126            }
127            _ => None,
128        };
129
130        Ok(Self {
131            supply: mint.base.supply,
132            decimals: mint.base.decimals,
133            metadata: mint.metadata,
134            mint_authority: mint.base.mint_authority,
135            freeze_authority: mint.base.freeze_authority,
136            extensions,
137        })
138    }
139}
140
141impl<'a> TryFrom<&ZMintInstructionData<'a>> for Mint {
142    type Error = TokenError;
143
144    fn try_from(instruction_data: &ZMintInstructionData<'a>) -> Result<Self, Self::Error> {
145        let extensions = match &instruction_data.extensions {
146            Some(exts) => {
147                let converted_exts: Vec<_> = exts
148                    .iter()
149                    .map(|ext| match ext {
150                        ZExtensionInstructionData::TokenMetadata(token_metadata_data) => {
151                            Ok(ExtensionStruct::TokenMetadata(TokenMetadata {
152                                update_authority: token_metadata_data
153                                    .update_authority
154                                    .map(|p| *p)
155                                    .unwrap_or_else(|| Pubkey::from([0u8; 32])),
156                                mint: instruction_data.metadata.mint, // Use the mint from metadata
157                                name: token_metadata_data.name.to_vec(),
158                                symbol: token_metadata_data.symbol.to_vec(),
159                                uri: token_metadata_data.uri.to_vec(),
160                                additional_metadata: token_metadata_data
161                                    .additional_metadata
162                                    .as_ref()
163                                    .map(|ams| {
164                                        ams.iter()
165                                            .map(|am| AdditionalMetadata {
166                                                key: am.key.to_vec(),
167                                                value: am.value.to_vec(),
168                                            })
169                                            .collect()
170                                    })
171                                    .unwrap_or_else(Vec::new),
172                            }))
173                        }
174                        _ => Err(TokenError::UnsupportedExtension),
175                    })
176                    .collect::<Result<Vec<_>, _>>()?;
177                if converted_exts.is_empty() {
178                    None
179                } else {
180                    Some(converted_exts)
181                }
182            }
183            None => None,
184        };
185
186        Ok(Self {
187            base: BaseMint {
188                mint_authority: instruction_data.mint_authority.map(|p| *p),
189                supply: instruction_data.supply.into(),
190                decimals: instruction_data.decimals,
191                is_initialized: true, // Always true for compressed mints
192                freeze_authority: instruction_data.freeze_authority.map(|p| *p),
193            },
194            metadata: MintMetadata {
195                version: instruction_data.metadata.version,
196                mint_decompressed: instruction_data.metadata.mint_decompressed != 0,
197                mint: instruction_data.metadata.mint,
198                mint_signer: instruction_data.metadata.mint_signer,
199                bump: instruction_data.metadata.bump,
200            },
201            reserved: [0u8; 16],
202            account_type: crate::state::mint::ACCOUNT_TYPE_MINT,
203            compression: CompressionInfo::default(),
204            extensions,
205        })
206    }
207}