Skip to main content

light_token_client/actions/
transfer_interface.rs

1//! Transfer interface action for Light Token.
2//!
3//! This action provides a clean interface for transferring tokens that auto-routes
4//! based on the account types (Light or SPL).
5
6use light_client::rpc::{Rpc, RpcError};
7use light_token::{
8    instruction::{SplInterface, TransferInterface as TransferInterfaceInstruction},
9    spl_interface::find_spl_interface_pda_with_index,
10};
11use solana_keypair::Keypair;
12use solana_pubkey::Pubkey;
13use solana_signature::Signature;
14use solana_signer::Signer;
15
16/// Parameters for transferring tokens using the interface that auto-routes based on account types.
17///
18/// This automatically detects whether the source and destination are
19/// Light token accounts or SPL token accounts and routes the transfer accordingly:
20/// - Light -> Light: Direct transfer between Light accounts
21/// - Light -> SPL: Decompress from Light to SPL account
22/// - SPL -> Light: Compress from SPL to Light account
23/// - SPL -> SPL: Pass-through to SPL token program
24///
25/// # Example
26/// ```ignore
27/// TransferInterface {
28///     source,
29///     mint,
30///     destination,
31///     amount: 1000,
32///     decimals: 9,
33///     ..Default::default()
34/// }.execute(&mut rpc, &payer, &authority).await?;
35/// ```
36#[derive(Default, Clone, Debug)]
37pub struct TransferInterface {
38    /// Source token account.
39    pub source: Pubkey,
40    /// The mint public key.
41    pub mint: Pubkey,
42    /// Destination token account.
43    pub destination: Pubkey,
44    /// Amount of tokens to transfer.
45    pub amount: u64,
46    /// Token decimals.
47    pub decimals: u8,
48    /// SPL token program (spl_token::ID or spl_token_2022::ID), required for cross-interface transfers.
49    pub spl_token_program: Option<Pubkey>,
50    /// Whether the mint has restricted extensions (Token-2022 specific).
51    pub restricted: bool,
52}
53
54impl TransferInterface {
55    /// Execute the transfer_interface action via RPC.
56    ///
57    /// # Arguments
58    /// * `rpc` - RPC client
59    /// * `payer` - Transaction fee payer keypair
60    /// * `authority` - Authority that can spend from the source account
61    ///
62    /// # Returns
63    /// `Result<Signature, RpcError>` - The transaction signature
64    pub async fn execute<R: Rpc>(
65        self,
66        rpc: &mut R,
67        payer: &Keypair,
68        authority: &Keypair,
69    ) -> Result<Signature, RpcError> {
70        // Fetch account info to determine owners
71        let source_account = rpc.get_account(self.source).await?.ok_or_else(|| {
72            RpcError::CustomError(format!("Source account {} not found", self.source))
73        })?;
74
75        let destination_account = rpc.get_account(self.destination).await?.ok_or_else(|| {
76            RpcError::CustomError(format!(
77                "Destination account {} not found",
78                self.destination
79            ))
80        })?;
81
82        let source_owner = source_account.owner;
83        let destination_owner = destination_account.owner;
84
85        // Build SplInterface if needed for cross-interface transfers
86        let spl_interface = if let Some(spl_program) = self.spl_token_program {
87            let (spl_interface_pda, spl_interface_pda_bump) =
88                find_spl_interface_pda_with_index(&self.mint, 0, self.restricted);
89            Some(SplInterface {
90                mint: self.mint,
91                spl_token_program: spl_program,
92                spl_interface_pda,
93                spl_interface_pda_bump,
94            })
95        } else {
96            None
97        };
98
99        let ix = TransferInterfaceInstruction {
100            source: self.source,
101            destination: self.destination,
102            amount: self.amount,
103            decimals: self.decimals,
104            authority: authority.pubkey(),
105            payer: payer.pubkey(),
106            spl_interface,
107            max_top_up: None,
108            source_owner,
109            destination_owner,
110        }
111        .instruction()
112        .map_err(|e| RpcError::CustomError(format!("Failed to create instruction: {}", e)))?;
113
114        let mut signers = vec![payer];
115        if authority.pubkey() != payer.pubkey() {
116            signers.push(authority);
117        }
118
119        rpc.create_and_send_transaction(&[ix], &payer.pubkey(), &signers)
120            .await
121    }
122}