Skip to main content

light_token_client/actions/
unwrap.rs

1//! Unwrap Light Token to SPL tokens action.
2//!
3//! Unwraps Light Token back to an SPL token account.
4
5use light_client::rpc::{Rpc, RpcError};
6use light_token::{
7    constants::{SPL_TOKEN_2022_PROGRAM_ID, SPL_TOKEN_PROGRAM_ID},
8    instruction::TransferToSpl,
9    spl_interface::{find_spl_interface_pda, has_restricted_extensions},
10};
11use solana_keypair::Keypair;
12use solana_pubkey::Pubkey;
13use solana_signature::Signature;
14use solana_signer::Signer;
15
16/// Parameters for unwrapping Light Token back to SPL tokens.
17///
18/// This transfers tokens from a Light Token account to an SPL token account.
19///
20/// # Example
21/// ```ignore
22/// Unwrap {
23///     source,
24///     destination_spl_ata,
25///     mint,
26///     amount: 1000,
27///     decimals: 9,
28/// }.execute(&mut rpc, &payer, &authority).await?;
29/// ```
30#[derive(Default, Clone, Debug)]
31pub struct Unwrap {
32    /// Source Light Token account.
33    pub source: Pubkey,
34    /// Destination SPL token account.
35    pub destination_spl_ata: Pubkey,
36    /// The mint public key.
37    pub mint: Pubkey,
38    /// Amount of tokens to unwrap.
39    pub amount: u64,
40    /// Token decimals.
41    pub decimals: u8,
42}
43
44impl Unwrap {
45    /// Execute the unwrap action via RPC.
46    ///
47    /// # Arguments
48    /// * `rpc` - RPC client
49    /// * `payer` - Transaction fee payer keypair
50    /// * `authority` - Authority for the source Light Token account
51    ///
52    /// # Returns
53    /// `Result<Signature, RpcError>` - The transaction signature
54    pub async fn execute<R: Rpc>(
55        self,
56        rpc: &mut R,
57        payer: &Keypair,
58        authority: &Keypair,
59    ) -> Result<Signature, RpcError> {
60        // Get the destination account to determine the token program
61        let destination_account_info = rpc
62            .get_account(self.destination_spl_ata)
63            .await?
64            .ok_or_else(|| {
65                RpcError::CustomError("Destination SPL token account not found".to_string())
66            })?;
67
68        let spl_token_program = destination_account_info.owner;
69
70        // Validate that the destination account is owned by a supported SPL token program
71        if spl_token_program != SPL_TOKEN_PROGRAM_ID
72            && spl_token_program != SPL_TOKEN_2022_PROGRAM_ID
73        {
74            return Err(RpcError::CustomError(format!(
75                "Destination SPL token account {} is owned by an unsupported program {}. \
76                 Expected SPL Token ({}) or Token-2022 ({}).",
77                self.destination_spl_ata,
78                destination_account_info.owner,
79                SPL_TOKEN_PROGRAM_ID,
80                SPL_TOKEN_2022_PROGRAM_ID
81            )));
82        }
83
84        // Check for restricted extensions if using Token-2022
85        let restricted = if spl_token_program == SPL_TOKEN_2022_PROGRAM_ID {
86            let mint_account = rpc
87                .get_account(self.mint)
88                .await?
89                .ok_or_else(|| RpcError::CustomError("Mint account not found".to_string()))?;
90            has_restricted_extensions(&mint_account.data)
91        } else {
92            false
93        };
94
95        let (spl_interface_pda, bump) = find_spl_interface_pda(&self.mint, restricted);
96
97        let ix = TransferToSpl {
98            source: self.source,
99            destination_spl_token_account: self.destination_spl_ata,
100            amount: self.amount,
101            authority: authority.pubkey(),
102            mint: self.mint,
103            payer: payer.pubkey(),
104            spl_interface_pda,
105            spl_interface_pda_bump: bump,
106            decimals: self.decimals,
107            spl_token_program,
108        }
109        .instruction()
110        .map_err(|e| RpcError::CustomError(format!("Failed to create instruction: {}", e)))?;
111
112        let mut signers: Vec<&Keypair> = vec![payer];
113        if authority.pubkey() != payer.pubkey() {
114            signers.push(authority);
115        }
116
117        rpc.create_and_send_transaction(&[ix], &payer.pubkey(), &signers)
118            .await
119    }
120}