light_token/instruction/
transfer_checked.rs1use light_sdk_types::LIGHT_TOKEN_PROGRAM_ID;
2use solana_account_info::AccountInfo;
3use solana_cpi::{invoke, invoke_signed};
4use solana_instruction::{AccountMeta, Instruction};
5use solana_program_error::ProgramError;
6use solana_pubkey::Pubkey;
7
8pub struct TransferChecked {
29 pub source: Pubkey,
30 pub mint: Pubkey,
31 pub destination: Pubkey,
32 pub amount: u64,
33 pub decimals: u8,
34 pub authority: Pubkey,
35 pub max_top_up: Option<u16>,
38 pub fee_payer: Option<Pubkey>,
40}
41
42pub struct TransferCheckedCpi<'info> {
66 pub source: AccountInfo<'info>,
67 pub mint: AccountInfo<'info>,
68 pub destination: AccountInfo<'info>,
69 pub amount: u64,
70 pub decimals: u8,
71 pub authority: AccountInfo<'info>,
72 pub system_program: AccountInfo<'info>,
73 pub max_top_up: Option<u16>,
75 pub fee_payer: Option<AccountInfo<'info>>,
77}
78
79impl<'info> TransferCheckedCpi<'info> {
80 pub fn instruction(&self) -> Result<Instruction, ProgramError> {
81 TransferChecked::from(self).instruction()
82 }
83
84 pub fn invoke(self) -> Result<(), ProgramError> {
85 let instruction = TransferChecked::from(&self).instruction()?;
86 if let Some(fee_payer) = self.fee_payer {
87 let account_infos = [
88 self.source,
89 self.mint,
90 self.destination,
91 self.authority,
92 self.system_program,
93 fee_payer,
94 ];
95 invoke(&instruction, &account_infos)
96 } else {
97 let account_infos = [
98 self.source,
99 self.mint,
100 self.destination,
101 self.authority,
102 self.system_program,
103 ];
104 invoke(&instruction, &account_infos)
105 }
106 }
107
108 pub fn invoke_signed(self, signer_seeds: &[&[&[u8]]]) -> Result<(), ProgramError> {
109 let instruction = TransferChecked::from(&self).instruction()?;
110 if let Some(fee_payer) = self.fee_payer {
111 let account_infos = [
112 self.source,
113 self.mint,
114 self.destination,
115 self.authority,
116 self.system_program,
117 fee_payer,
118 ];
119 invoke_signed(&instruction, &account_infos, signer_seeds)
120 } else {
121 let account_infos = [
122 self.source,
123 self.mint,
124 self.destination,
125 self.authority,
126 self.system_program,
127 ];
128 invoke_signed(&instruction, &account_infos, signer_seeds)
129 }
130 }
131}
132
133impl<'info> From<&TransferCheckedCpi<'info>> for TransferChecked {
134 fn from(account_infos: &TransferCheckedCpi<'info>) -> Self {
135 Self {
136 source: *account_infos.source.key,
137 mint: *account_infos.mint.key,
138 destination: *account_infos.destination.key,
139 amount: account_infos.amount,
140 decimals: account_infos.decimals,
141 authority: *account_infos.authority.key,
142 max_top_up: account_infos.max_top_up,
143 fee_payer: account_infos.fee_payer.as_ref().map(|a| *a.key),
144 }
145 }
146}
147
148impl TransferChecked {
149 pub fn instruction(self) -> Result<Instruction, ProgramError> {
150 let authority_meta = if self.max_top_up.is_some() && self.fee_payer.is_none() {
153 AccountMeta::new(self.authority, true)
154 } else {
155 AccountMeta::new_readonly(self.authority, true)
156 };
157
158 let mut accounts = vec![
159 AccountMeta::new(self.source, false),
160 AccountMeta::new_readonly(self.mint, false),
161 AccountMeta::new(self.destination, false),
162 authority_meta,
163 AccountMeta::new_readonly(Pubkey::default(), false),
165 ];
166
167 if let Some(fee_payer) = self.fee_payer {
169 accounts.push(AccountMeta::new(fee_payer, true));
170 }
171
172 Ok(Instruction {
173 program_id: Pubkey::from(LIGHT_TOKEN_PROGRAM_ID),
174 accounts,
175 data: {
176 let mut data = vec![12u8]; data.extend_from_slice(&self.amount.to_le_bytes());
179 data.push(self.decimals);
180 if let Some(max_top_up) = self.max_top_up {
182 data.extend_from_slice(&max_top_up.to_le_bytes());
183 }
184 data
185 },
186 })
187 }
188}