1use {
4 crate::program::account,
5 solana_program::{
6 account_info::AccountInfo, entrypoint::ProgramResult, program, program_error::ProgramError,
7 program_pack::Pack, pubkey::Pubkey, rent::Rent, system_instruction, sysvar, sysvar::Sysvar,
8 },
9};
10
11pub fn init_token_account<'a, 'b>(
12 funding_account: &'a AccountInfo<'b>,
13 target_account: &'a AccountInfo<'b>,
14 mint_account: &'a AccountInfo<'b>,
15 owner_account: &'a AccountInfo<'b>,
16 rent_program: &'a AccountInfo<'b>,
17 base_address: &Pubkey,
18 seeds: &[&[u8]],
19) -> ProgramResult {
20 if account::exists(target_account)? {
21 if !account::check_token_account_owner(target_account, owner_account.key)? {
22 return Err(ProgramError::IllegalOwner);
23 }
24 if target_account.data_len() != spl_token::state::Account::get_packed_len()
25 || mint_account.key != &account::get_token_account_mint(target_account)?
26 {
27 return Err(ProgramError::InvalidAccountData);
28 }
29 return Ok(());
30 }
31
32 init_system_account(
33 funding_account,
34 target_account,
35 &spl_token::id(),
36 base_address,
37 seeds,
38 spl_token::state::Account::get_packed_len(),
39 )?;
40
41 program::invoke(
42 &spl_token::instruction::initialize_account(
43 &spl_token::id(),
44 target_account.key,
45 mint_account.key,
46 owner_account.key,
47 )?,
48 &[
49 target_account.clone(),
50 mint_account.clone(),
51 owner_account.clone(),
52 rent_program.clone(),
53 ],
54 )
55}
56
57pub fn init_associated_token_account<'a, 'b>(
58 funding_account: &'a AccountInfo<'b>,
59 wallet_account: &'a AccountInfo<'b>,
60 target_account: &'a AccountInfo<'b>,
61 mint_account: &'a AccountInfo<'b>,
62 rent_program: &'a AccountInfo<'b>,
63) -> ProgramResult {
64 if account::exists(target_account)? {
65 if !account::check_token_account_owner(target_account, wallet_account.key)? {
66 return Err(ProgramError::IllegalOwner);
67 }
68 if target_account.data_len() != spl_token::state::Account::get_packed_len()
69 || mint_account.key != &account::get_token_account_mint(target_account)?
70 {
71 return Err(ProgramError::InvalidAccountData);
72 }
73 return Ok(());
74 }
75
76 program::invoke(
77 &spl_associated_token_account::create_associated_token_account(
78 funding_account.key,
79 wallet_account.key,
80 mint_account.key,
81 ),
82 &[
83 funding_account.clone(),
84 target_account.clone(),
85 wallet_account.clone(),
86 mint_account.clone(),
87 rent_program.clone(),
88 ],
89 )
90}
91
92pub fn close_token_account_with_seeds<'a, 'b>(
93 receiving_account: &'a AccountInfo<'b>,
94 target_account: &'a AccountInfo<'b>,
95 authority_account: &'a AccountInfo<'b>,
96 seeds: &[&[&[u8]]],
97) -> ProgramResult {
98 if !account::exists(target_account)? {
99 return Ok(());
100 }
101
102 program::invoke_signed(
103 &spl_token::instruction::close_account(
104 &spl_token::id(),
105 target_account.key,
106 receiving_account.key,
107 authority_account.key,
108 &[],
109 )?,
110 &[
111 target_account.clone(),
112 receiving_account.clone(),
113 authority_account.clone(),
114 ],
115 seeds,
116 )
117}
118
119pub fn close_token_account<'a, 'b>(
120 receiving_account: &'a AccountInfo<'b>,
121 target_account: &'a AccountInfo<'b>,
122 authority_account: &'a AccountInfo<'b>,
123 base_address: &Pubkey,
124 seeds: &[&[u8]],
125) -> Result<u8, ProgramError> {
126 let (_, bump) = Pubkey::find_program_address(seeds, base_address);
127
128 close_token_account_with_seeds(
129 receiving_account,
130 target_account,
131 authority_account,
132 &[&[seeds, &[&[bump]]].concat()],
133 )?;
134
135 Ok(bump)
136}
137
138pub fn transfer_tokens_with_seeds<'a, 'b>(
139 source_account: &'a AccountInfo<'b>,
140 destination_account: &'a AccountInfo<'b>,
141 authority_account: &'a AccountInfo<'b>,
142 seeds: &[&[&[u8]]],
143 amount: u64,
144) -> ProgramResult {
145 if source_account.key == destination_account.key {
146 return Err(ProgramError::InvalidArgument);
147 }
148 program::invoke_signed(
149 &spl_token::instruction::transfer(
150 &spl_token::id(),
151 source_account.key,
152 destination_account.key,
153 authority_account.key,
154 &[],
155 amount,
156 )?,
157 &[
158 source_account.clone(),
159 destination_account.clone(),
160 authority_account.clone(),
161 ],
162 seeds,
163 )
164}
165
166pub fn transfer_tokens<'a, 'b>(
167 source_account: &'a AccountInfo<'b>,
168 destination_account: &'a AccountInfo<'b>,
169 authority_account: &'a AccountInfo<'b>,
170 base_address: &Pubkey,
171 seeds: &[&[u8]],
172 amount: u64,
173) -> Result<u8, ProgramError> {
174 let (_, bump) = Pubkey::find_program_address(seeds, base_address);
175
176 transfer_tokens_with_seeds(
177 source_account,
178 destination_account,
179 authority_account,
180 &[&[seeds, &[&[bump]]].concat()],
181 amount,
182 )?;
183
184 Ok(bump)
185}
186
187pub fn init_system_account<'a, 'b>(
188 funding_account: &'a AccountInfo<'b>,
189 target_account: &'a AccountInfo<'b>,
190 owner_key: &Pubkey,
191 base_address: &Pubkey,
192 seeds: &[&[u8]],
193 data_size: usize,
194) -> Result<u8, ProgramError> {
195 if account::exists(target_account)? {
196 if target_account.owner != owner_key {
197 return Err(ProgramError::IllegalOwner);
198 }
199 if target_account.data_len() != data_size {
200 return Err(ProgramError::InvalidAccountData);
201 }
202 return Ok(Pubkey::find_program_address(seeds, base_address).1);
203 }
204
205 let (key, bump) = Pubkey::find_program_address(seeds, base_address);
206 if target_account.key != &key {
207 return Err(ProgramError::InvalidSeeds);
208 }
209
210 let min_balance = sysvar::rent::Rent::get()
211 .unwrap()
212 .minimum_balance(data_size);
213 program::invoke_signed(
214 &system_instruction::create_account(
215 funding_account.key,
216 target_account.key,
217 min_balance,
218 data_size as u64,
219 owner_key,
220 ),
221 &[funding_account.clone(), target_account.clone()],
222 &[&[seeds, &[&[bump]]].concat()],
223 )?;
224
225 Ok(bump)
226}
227
228pub fn init_mint<'a, 'b>(
229 funding_account: &'a AccountInfo<'b>,
230 mint_account: &'a AccountInfo<'b>,
231 authority_account: &'a AccountInfo<'b>,
232 rent_program: &'a AccountInfo<'b>,
233 base_address: &Pubkey,
234 seeds: &[&[u8]],
235 decimals: u8,
236) -> ProgramResult {
237 if account::exists(mint_account)? {
238 if !account::check_mint_authority(mint_account, Some(*authority_account.key))? {
239 return Err(ProgramError::IllegalOwner);
240 }
241 if mint_account.data_len() != spl_token::state::Mint::get_packed_len() {
242 return Err(ProgramError::InvalidAccountData);
243 }
244 return Ok(());
245 }
246
247 let acc_size = spl_token::state::Mint::get_packed_len();
248 init_system_account(
249 funding_account,
250 mint_account,
251 &spl_token::id(),
252 base_address,
253 seeds,
254 acc_size,
255 )?;
256
257 program::invoke(
258 &spl_token::instruction::initialize_mint(
259 &spl_token::id(),
260 mint_account.key,
261 authority_account.key,
262 Some(authority_account.key),
263 decimals,
264 )?,
265 &[
266 mint_account.clone(),
267 authority_account.clone(),
268 rent_program.clone(),
269 ],
270 )
271}
272
273pub fn mint_to_with_seeds<'a, 'b>(
274 target_token_account: &'a AccountInfo<'b>,
275 mint_account: &'a AccountInfo<'b>,
276 mint_authority_account: &'a AccountInfo<'b>,
277 seeds: &[&[&[u8]]],
278 amount: u64,
279) -> ProgramResult {
280 solana_program::program::invoke_signed(
281 &spl_token::instruction::mint_to(
282 &spl_token::id(),
283 mint_account.key,
284 target_token_account.key,
285 mint_authority_account.key,
286 &[],
287 amount,
288 )?,
289 &[
290 mint_account.clone(),
291 target_token_account.clone(),
292 mint_authority_account.clone(),
293 ],
294 seeds,
295 )
296}
297
298pub fn mint_to<'a, 'b>(
299 target_token_account: &'a AccountInfo<'b>,
300 mint_account: &'a AccountInfo<'b>,
301 mint_authority_account: &'a AccountInfo<'b>,
302 base_address: &Pubkey,
303 seeds: &[&[u8]],
304 amount: u64,
305) -> Result<u8, ProgramError> {
306 let (_, bump) = Pubkey::find_program_address(seeds, base_address);
307
308 mint_to_with_seeds(
309 target_token_account,
310 mint_account,
311 mint_authority_account,
312 &[&[seeds, &[&[bump]]].concat()],
313 amount,
314 )?;
315
316 Ok(bump)
317}
318
319pub fn burn_tokens_with_seeds<'a, 'b>(
320 from_token_account: &'a AccountInfo<'b>,
321 mint_account: &'a AccountInfo<'b>,
322 authority_account: &'a AccountInfo<'b>,
323 seeds: &[&[&[u8]]],
324 amount: u64,
325) -> ProgramResult {
326 solana_program::program::invoke_signed(
327 &spl_token::instruction::burn(
328 &spl_token::id(),
329 from_token_account.key,
330 mint_account.key,
331 authority_account.key,
332 &[],
333 amount,
334 )?,
335 &[
336 from_token_account.clone(),
337 mint_account.clone(),
338 authority_account.clone(),
339 ],
340 seeds,
341 )
342}
343
344pub fn burn_tokens<'a, 'b>(
345 from_token_account: &'a AccountInfo<'b>,
346 mint_account: &'a AccountInfo<'b>,
347 authority_account: &'a AccountInfo<'b>,
348 base_address: &Pubkey,
349 seeds: &[&[u8]],
350 amount: u64,
351) -> Result<u8, ProgramError> {
352 let (_, bump) = Pubkey::find_program_address(seeds, base_address);
353
354 burn_tokens_with_seeds(
355 from_token_account,
356 mint_account,
357 authority_account,
358 &[&[seeds, &[&[bump]]].concat()],
359 amount,
360 )?;
361
362 Ok(bump)
363}
364
365pub fn approve_delegate_with_seeds<'a, 'b>(
366 source_account: &'a AccountInfo<'b>,
367 delegate_account: &'a AccountInfo<'b>,
368 authority_account: &'a AccountInfo<'b>,
369 seeds: &[&[&[u8]]],
370 amount: u64,
371) -> ProgramResult {
372 solana_program::program::invoke_signed(
373 &spl_token::instruction::approve(
374 &spl_token::id(),
375 source_account.key,
376 delegate_account.key,
377 authority_account.key,
378 &[],
379 amount,
380 )?,
381 &[
382 source_account.clone(),
383 delegate_account.clone(),
384 authority_account.clone(),
385 ],
386 seeds,
387 )
388}
389
390pub fn approve_delegate<'a, 'b>(
391 source_account: &'a AccountInfo<'b>,
392 delegate_account: &'a AccountInfo<'b>,
393 authority_account: &'a AccountInfo<'b>,
394 base_address: &Pubkey,
395 seeds: &[&[u8]],
396 amount: u64,
397) -> Result<u8, ProgramError> {
398 let (_, bump) = Pubkey::find_program_address(seeds, base_address);
399
400 approve_delegate_with_seeds(
401 source_account,
402 delegate_account,
403 authority_account,
404 &[&[seeds, &[&[bump]]].concat()],
405 amount,
406 )?;
407
408 Ok(bump)
409}
410
411pub fn revoke_delegate_with_seeds<'a, 'b>(
412 source_account: &'a AccountInfo<'b>,
413 authority_account: &'a AccountInfo<'b>,
414 seeds: &[&[&[u8]]],
415) -> ProgramResult {
416 solana_program::program::invoke_signed(
417 &spl_token::instruction::revoke(
418 &spl_token::id(),
419 source_account.key,
420 authority_account.key,
421 &[],
422 )?,
423 &[source_account.clone(), authority_account.clone()],
424 seeds,
425 )
426}
427
428pub fn revoke_delegate<'a, 'b>(
429 source_account: &'a AccountInfo<'b>,
430 authority_account: &'a AccountInfo<'b>,
431 base_address: &Pubkey,
432 seeds: &[&[u8]],
433) -> Result<u8, ProgramError> {
434 let (_, bump) = Pubkey::find_program_address(seeds, base_address);
435
436 revoke_delegate_with_seeds(
437 source_account,
438 authority_account,
439 &[&[seeds, &[&[bump]]].concat()],
440 )?;
441
442 Ok(bump)
443}
444
445pub fn check_pda_data_size<'a, 'b>(
446 target_account: &'a AccountInfo<'b>,
447 seeds: &[&[u8]],
448 data_size: usize,
449 fix: bool,
450) -> ProgramResult {
451 if fix && target_account.data_is_empty() {
452 program::invoke_signed(
453 &system_instruction::allocate(target_account.key, data_size as u64),
454 &[target_account.clone()],
455 &[seeds],
456 )?;
457 }
458 if target_account.data_len() < data_size {
459 Err(ProgramError::AccountDataTooSmall)
460 } else {
461 Ok(())
462 }
463}
464
465pub fn check_pda_rent_exempt<'a, 'b>(
466 signer_account: &'a AccountInfo<'b>,
467 target_account: &'a AccountInfo<'b>,
468 seeds: &[&[u8]],
469 data_size: usize,
470 fix: bool,
471) -> ProgramResult {
472 let rent = Rent::get()?;
473 let cur_balance = target_account.try_lamports()?;
474 let min_balance = rent.minimum_balance(data_size);
475 if cur_balance < min_balance {
476 let signer_balance = signer_account.try_lamports()?;
477 let signer_min_balance = rent.minimum_balance(signer_account.data_len());
478 if !fix
479 || signer_balance <= signer_min_balance
480 || min_balance.checked_sub(cur_balance).unwrap()
481 > signer_balance.checked_sub(signer_min_balance).unwrap()
482 {
483 return Err(ProgramError::InsufficientFunds);
484 }
485 program::invoke_signed(
486 &system_instruction::transfer(
487 signer_account.key,
488 target_account.key,
489 min_balance.checked_sub(cur_balance).unwrap(),
490 ),
491 &[signer_account.clone(), target_account.clone()],
492 &[seeds],
493 )?;
494 assert!(target_account.try_lamports()? >= min_balance);
495 }
496 Ok(())
497}
498
499pub fn check_pda_owner<'a, 'b>(
500 program_id: &Pubkey,
501 target_account: &'a AccountInfo<'b>,
502 seeds: &[&[u8]],
503 fix: bool,
504) -> ProgramResult {
505 if *target_account.owner != *program_id {
506 if fix {
507 program::invoke_signed(
508 &system_instruction::assign(target_account.key, program_id),
509 &[target_account.clone()],
510 &[seeds],
511 )?;
512 assert!(*target_account.owner == *program_id);
513 } else {
514 return Err(ProgramError::IllegalOwner);
515 }
516 }
517 Ok(())
518}