1use borsh::BorshSerialize;
2use light_token_interface::instructions::{
3 create_token_account::CreateTokenAccountInstructionData,
4 extensions::{CompressToPubkey, CompressibleExtensionInstructionData},
5};
6use solana_account_info::AccountInfo;
7use solana_cpi::{invoke, invoke_signed};
8use solana_instruction::{AccountMeta, Instruction};
9use solana_program_error::ProgramError;
10use solana_pubkey::Pubkey;
11
12use crate::instruction::{compressible::CompressibleParamsCpi, CompressibleParams};
13
14#[derive(Debug, Clone)]
28pub struct CreateTokenAccount {
29 pub payer: Pubkey,
30 pub account: Pubkey,
31 pub mint: Pubkey,
32 pub owner: Pubkey,
33 pub compressible: CompressibleParams,
34}
35
36impl CreateTokenAccount {
37 pub fn new(payer: Pubkey, account: Pubkey, mint: Pubkey, owner: Pubkey) -> Self {
38 Self {
39 payer,
40 account,
41 mint,
42 owner,
43 compressible: CompressibleParams::default(),
44 }
45 }
46
47 pub fn with_compressible(mut self, compressible: CompressibleParams) -> Self {
48 self.compressible = compressible;
49 self
50 }
51
52 pub fn instruction(self) -> Result<Instruction, ProgramError> {
53 let instruction_data = CreateTokenAccountInstructionData {
54 owner: light_compressed_account::Pubkey::from(self.owner.to_bytes()),
55 compressible_config: Some(CompressibleExtensionInstructionData {
56 token_account_version: self.compressible.token_account_version as u8,
57 rent_payment: self.compressible.pre_pay_num_epochs,
58 compression_only: self.compressible.compression_only as u8,
59 write_top_up: self.compressible.lamports_per_write.unwrap_or(0),
60 compress_to_account_pubkey: self.compressible.compress_to_account_pubkey,
61 }),
62 };
63
64 let mut data = Vec::new();
65 data.push(18u8); instruction_data
67 .serialize(&mut data)
68 .map_err(|e| ProgramError::BorshIoError(e.to_string()))?;
69
70 let accounts = vec![
71 AccountMeta::new(self.account, true),
72 AccountMeta::new_readonly(self.mint, false),
73 AccountMeta::new(self.payer, true),
74 AccountMeta::new_readonly(self.compressible.compressible_config, false),
75 AccountMeta::new_readonly(Pubkey::default(), false), AccountMeta::new(self.compressible.rent_sponsor, false),
77 ];
78
79 Ok(Instruction {
80 program_id: Pubkey::from(light_token_interface::LIGHT_TOKEN_PROGRAM_ID),
81 accounts,
82 data,
83 })
84 }
85}
86
87pub struct CreateTokenAccountCpi<'info> {
106 pub payer: AccountInfo<'info>,
107 pub account: AccountInfo<'info>,
108 pub mint: AccountInfo<'info>,
109 pub owner: Pubkey,
110}
111
112impl<'info> CreateTokenAccountCpi<'info> {
113 pub fn rent_free(
119 self,
120 config: AccountInfo<'info>,
121 sponsor: AccountInfo<'info>,
122 system_program: AccountInfo<'info>,
123 program_id: &Pubkey,
124 ) -> CreateTokenAccountRentFreeCpi<'info> {
125 CreateTokenAccountRentFreeCpi {
126 base: self,
127 config,
128 sponsor,
129 system_program,
130 program_id: *program_id,
131 }
132 }
133
134 pub fn invoke_with(
136 self,
137 compressible: CompressibleParamsCpi<'info>,
138 ) -> Result<(), ProgramError> {
139 LegacyCreateTokenAccountCpi {
140 payer: self.payer,
141 account: self.account,
142 mint: self.mint,
143 owner: self.owner,
144 compressible,
145 }
146 .invoke()
147 }
148
149 pub fn invoke_signed_with(
151 self,
152 compressible: CompressibleParamsCpi<'info>,
153 signer_seeds: &[&[&[u8]]],
154 ) -> Result<(), ProgramError> {
155 LegacyCreateTokenAccountCpi {
156 payer: self.payer,
157 account: self.account,
158 mint: self.mint,
159 owner: self.owner,
160 compressible,
161 }
162 .invoke_signed(signer_seeds)
163 }
164}
165
166pub struct CreateTokenAccountRentFreeCpi<'info> {
168 base: CreateTokenAccountCpi<'info>,
169 config: AccountInfo<'info>,
170 sponsor: AccountInfo<'info>,
171 system_program: AccountInfo<'info>,
172 program_id: Pubkey,
173}
174
175impl<'info> CreateTokenAccountRentFreeCpi<'info> {
176 pub fn invoke(self) -> Result<(), ProgramError> {
178 let defaults = CompressibleParams::default();
179
180 let cpi = LegacyCreateTokenAccountCpi {
181 payer: self.base.payer,
182 account: self.base.account,
183 mint: self.base.mint,
184 owner: self.base.owner,
185 compressible: CompressibleParamsCpi {
186 compressible_config: self.config,
187 rent_sponsor: self.sponsor,
188 system_program: self.system_program,
189 pre_pay_num_epochs: defaults.pre_pay_num_epochs,
190 lamports_per_write: defaults.lamports_per_write,
191 compress_to_account_pubkey: None,
192 token_account_version: defaults.token_account_version,
193 compression_only: defaults.compression_only,
194 },
195 };
196 cpi.invoke()
197 }
198
199 pub fn invoke_signed(self, seeds: &[&[u8]]) -> Result<(), ProgramError> {
203 let defaults = CompressibleParams::default();
204
205 let bump = seeds.last().and_then(|s| s.first()).copied().unwrap_or(0);
207
208 let seed_vecs: Vec<Vec<u8>> = seeds
209 .iter()
210 .take(seeds.len().saturating_sub(1))
211 .map(|s| s.to_vec())
212 .collect();
213
214 let compress_to = CompressToPubkey {
215 bump,
216 program_id: self.program_id.to_bytes(),
217 seeds: seed_vecs,
218 };
219
220 let cpi = LegacyCreateTokenAccountCpi {
221 payer: self.base.payer,
222 account: self.base.account,
223 mint: self.base.mint,
224 owner: self.base.owner,
225 compressible: CompressibleParamsCpi {
226 compressible_config: self.config,
227 rent_sponsor: self.sponsor,
228 system_program: self.system_program,
229 pre_pay_num_epochs: defaults.pre_pay_num_epochs,
230 lamports_per_write: defaults.lamports_per_write,
231 compress_to_account_pubkey: Some(compress_to),
232 token_account_version: defaults.token_account_version,
233 compression_only: defaults.compression_only,
234 },
235 };
236 cpi.invoke_signed(&[seeds])
237 }
238}
239
240struct LegacyCreateTokenAccountCpi<'info> {
242 payer: AccountInfo<'info>,
243 account: AccountInfo<'info>,
244 mint: AccountInfo<'info>,
245 owner: Pubkey,
246 compressible: CompressibleParamsCpi<'info>,
247}
248
249impl<'info> LegacyCreateTokenAccountCpi<'info> {
250 fn instruction(&self) -> Result<Instruction, ProgramError> {
251 CreateTokenAccount {
252 payer: *self.payer.key,
253 account: *self.account.key,
254 mint: *self.mint.key,
255 owner: self.owner,
256 compressible: CompressibleParams {
257 compressible_config: *self.compressible.compressible_config.key,
258 rent_sponsor: *self.compressible.rent_sponsor.key,
259 pre_pay_num_epochs: self.compressible.pre_pay_num_epochs,
260 lamports_per_write: self.compressible.lamports_per_write,
261 compress_to_account_pubkey: self.compressible.compress_to_account_pubkey.clone(),
262 token_account_version: self.compressible.token_account_version,
263 compression_only: self.compressible.compression_only,
264 },
265 }
266 .instruction()
267 }
268
269 fn invoke(self) -> Result<(), ProgramError> {
270 let instruction = self.instruction()?;
271 let account_infos = [
272 self.account,
273 self.mint,
274 self.payer,
275 self.compressible.compressible_config,
276 self.compressible.system_program,
277 self.compressible.rent_sponsor,
278 ];
279 invoke(&instruction, &account_infos)
280 }
281
282 fn invoke_signed(self, signer_seeds: &[&[&[u8]]]) -> Result<(), ProgramError> {
283 let instruction = self.instruction()?;
284 let account_infos = [
285 self.account,
286 self.mint,
287 self.payer,
288 self.compressible.compressible_config,
289 self.compressible.system_program,
290 self.compressible.rent_sponsor,
291 ];
292 invoke_signed(&instruction, &account_infos, signer_seeds)
293 }
294}