Skip to main content

light_token_client/actions/
wrap.rs

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