1use litesvm::LiteSVM;
7use solana_keypair::Keypair;
8use solana_program::pubkey::Pubkey;
9use solana_signer::Signer;
10use solana_transaction::Transaction;
11use spl_associated_token_account::get_associated_token_address;
12use std::error::Error;
13
14pub trait TestHelpers {
16 fn create_funded_account(&mut self, lamports: u64) -> Result<Keypair, Box<dyn Error>>;
26
27 fn create_funded_accounts(
38 &mut self,
39 count: usize,
40 lamports: u64,
41 ) -> Result<Vec<Keypair>, Box<dyn Error>>;
42
43 fn create_token_mint(
55 &mut self,
56 authority: &Keypair,
57 decimals: u8,
58 ) -> Result<Keypair, Box<dyn Error>>;
59
60 fn create_token_account(
74 &mut self,
75 mint: &Pubkey,
76 owner: &Keypair,
77 ) -> Result<Keypair, Box<dyn Error>>;
78
79 fn create_associated_token_account(
93 &mut self,
94 mint: &Pubkey,
95 owner: &Keypair,
96 ) -> Result<Pubkey, Box<dyn Error>>;
97
98 fn mint_to(
114 &mut self,
115 mint: &Pubkey,
116 account: &Pubkey,
117 authority: &Keypair,
118 amount: u64,
119 ) -> Result<(), Box<dyn Error>>;
120
121 fn derive_pda(&self, seeds: &[&[u8]], program_id: &Pubkey) -> (Pubkey, u8);
133
134 fn get_pda(&self, seeds: &[&[u8]], program_id: &Pubkey) -> Pubkey {
157 let (pda, _bump) = self.derive_pda(seeds, program_id);
158 pda
159 }
160
161 fn get_pda_with_bump(&self, seeds: &[&[u8]], program_id: &Pubkey) -> (Pubkey, u8) {
173 self.derive_pda(seeds, program_id)
174 }
175
176 fn get_current_slot(&self) -> u64;
178
179 fn advance_slot(&mut self, slots: u64);
181}
182
183impl TestHelpers for LiteSVM {
184 fn create_funded_account(&mut self, lamports: u64) -> Result<Keypair, Box<dyn Error>> {
185 let keypair = Keypair::new();
186 self.airdrop(&keypair.pubkey(), lamports)
187 .map_err(|e| format!("Failed to airdrop: {:?}", e))?;
188 Ok(keypair)
189 }
190
191 fn create_funded_accounts(
192 &mut self,
193 count: usize,
194 lamports: u64,
195 ) -> Result<Vec<Keypair>, Box<dyn Error>> {
196 let mut accounts = Vec::with_capacity(count);
197 for _ in 0..count {
198 accounts.push(self.create_funded_account(lamports)?);
199 }
200 Ok(accounts)
201 }
202
203 fn create_token_mint(
204 &mut self,
205 authority: &Keypair,
206 decimals: u8,
207 ) -> Result<Keypair, Box<dyn Error>> {
208 let mint = Keypair::new();
209
210 let rent = self.minimum_balance_for_rent_exemption(82);
212
213 let create_account_ix = solana_system_interface::instruction::create_account(
215 &authority.pubkey(),
216 &mint.pubkey(),
217 rent,
218 82,
219 &spl_token::id(),
220 );
221
222 let init_mint_ix = spl_token::instruction::initialize_mint(
224 &spl_token::id(),
225 &mint.pubkey(),
226 &authority.pubkey(),
227 None,
228 decimals,
229 )?;
230
231 let tx = Transaction::new_signed_with_payer(
233 &[create_account_ix, init_mint_ix],
234 Some(&authority.pubkey()),
235 &[authority, &mint],
236 self.latest_blockhash(),
237 );
238
239 self.send_transaction(tx)
240 .map_err(|e| format!("Failed to create mint: {:?}", e.err))?;
241 Ok(mint)
242 }
243
244 fn create_token_account(
245 &mut self,
246 mint: &Pubkey,
247 owner: &Keypair,
248 ) -> Result<Keypair, Box<dyn Error>> {
249 let token_account = Keypair::new();
250
251 let rent = self.minimum_balance_for_rent_exemption(165);
253
254 let create_account_ix = solana_system_interface::instruction::create_account(
256 &owner.pubkey(),
257 &token_account.pubkey(),
258 rent,
259 165,
260 &spl_token::id(),
261 );
262
263 let init_account_ix = spl_token::instruction::initialize_account(
265 &spl_token::id(),
266 &token_account.pubkey(),
267 mint,
268 &owner.pubkey(),
269 )?;
270
271 let tx = Transaction::new_signed_with_payer(
273 &[create_account_ix, init_account_ix],
274 Some(&owner.pubkey()),
275 &[owner, &token_account],
276 self.latest_blockhash(),
277 );
278
279 self.send_transaction(tx)
280 .map_err(|e| format!("Failed to create token account: {:?}", e.err))?;
281 Ok(token_account)
282 }
283
284 fn create_associated_token_account(
285 &mut self,
286 mint: &Pubkey,
287 owner: &Keypair,
288 ) -> Result<Pubkey, Box<dyn Error>> {
289 let ata = get_associated_token_address(&owner.pubkey(), mint);
290
291 let create_ata_ix =
293 spl_associated_token_account::instruction::create_associated_token_account(
294 &owner.pubkey(),
295 &owner.pubkey(),
296 mint,
297 &spl_token::id(),
298 );
299
300 let tx = Transaction::new_signed_with_payer(
302 &[create_ata_ix],
303 Some(&owner.pubkey()),
304 &[owner],
305 self.latest_blockhash(),
306 );
307
308 self.send_transaction(tx)
309 .map_err(|e| format!("Failed to create ATA: {:?}", e.err))?;
310 Ok(ata)
311 }
312
313 fn mint_to(
314 &mut self,
315 mint: &Pubkey,
316 account: &Pubkey,
317 authority: &Keypair,
318 amount: u64,
319 ) -> Result<(), Box<dyn Error>> {
320 let mint_to_ix = spl_token::instruction::mint_to(
322 &spl_token::id(),
323 mint,
324 account,
325 &authority.pubkey(),
326 &[],
327 amount,
328 )?;
329
330 let tx = Transaction::new_signed_with_payer(
332 &[mint_to_ix],
333 Some(&authority.pubkey()),
334 &[authority],
335 self.latest_blockhash(),
336 );
337
338 self.send_transaction(tx)
339 .map_err(|e| format!("Failed to mint tokens: {:?}", e.err))?;
340 Ok(())
341 }
342
343 fn derive_pda(&self, seeds: &[&[u8]], program_id: &Pubkey) -> (Pubkey, u8) {
344 Pubkey::find_program_address(seeds, program_id)
345 }
346
347 fn get_current_slot(&self) -> u64 {
348 self.get_sysvar::<solana_program::clock::Clock>().slot
350 }
351
352 fn advance_slot(&mut self, slots: u64) {
353 let current_slot = self.get_sysvar::<solana_program::clock::Clock>().slot;
354 for i in 0..slots {
355 self.warp_to_slot(current_slot + i + 1);
356 }
357 }
358}
359
360#[cfg(test)]
361mod tests {
362 use super::*;
363 use solana_program_pack::Pack;
364 use solana_signer::Signer;
365
366 #[test]
367 fn test_create_funded_account() {
368 let mut svm = LiteSVM::new();
369 let lamports = 1_000_000_000;
370
371 let account = svm.create_funded_account(lamports).unwrap();
372
373 let balance = svm.get_balance(&account.pubkey()).unwrap();
375 assert_eq!(balance, lamports);
376 }
377
378 #[test]
379 fn test_create_funded_accounts() {
380 let mut svm = LiteSVM::new();
381 let count = 5;
382 let lamports = 500_000_000;
383
384 let accounts = svm.create_funded_accounts(count, lamports).unwrap();
385
386 assert_eq!(accounts.len(), count);
388
389 for account in &accounts {
391 let balance = svm.get_balance(&account.pubkey()).unwrap();
392 assert_eq!(balance, lamports);
393 }
394
395 let mut pubkeys: Vec<_> = accounts.iter().map(|k| k.pubkey()).collect();
397 pubkeys.sort();
398 pubkeys.dedup();
399 assert_eq!(pubkeys.len(), count);
400 }
401
402 #[test]
403 fn test_create_token_mint() {
404 let mut svm = LiteSVM::new();
405 let authority = svm.create_funded_account(10_000_000_000).unwrap();
406 let decimals = 9;
407
408 let mint = svm.create_token_mint(&authority, decimals).unwrap();
409
410 let mint_account = svm.get_account(&mint.pubkey()).unwrap();
412 assert_eq!(mint_account.owner, spl_token::id());
413
414 let mint_data = spl_token::state::Mint::unpack(&mint_account.data).unwrap();
416 assert_eq!(mint_data.decimals, decimals);
417 assert_eq!(mint_data.mint_authority, Some(authority.pubkey()).into());
418 assert_eq!(mint_data.supply, 0);
419 }
420
421 #[test]
422 fn test_create_token_account() {
423 let mut svm = LiteSVM::new();
424 let owner = svm.create_funded_account(10_000_000_000).unwrap();
425 let mint = svm.create_token_mint(&owner, 9).unwrap();
426
427 let token_account = svm.create_token_account(&mint.pubkey(), &owner).unwrap();
428
429 let account = svm.get_account(&token_account.pubkey()).unwrap();
431 assert_eq!(account.owner, spl_token::id());
432
433 let token_data = spl_token::state::Account::unpack(&account.data).unwrap();
435 assert_eq!(token_data.mint, mint.pubkey());
436 assert_eq!(token_data.owner, owner.pubkey());
437 assert_eq!(token_data.amount, 0);
438 }
439
440 #[test]
441 fn test_create_associated_token_account() {
442 let mut svm = LiteSVM::new();
443 let owner = svm.create_funded_account(10_000_000_000).unwrap();
444 let mint = svm.create_token_mint(&owner, 9).unwrap();
445
446 let ata = svm
447 .create_associated_token_account(&mint.pubkey(), &owner)
448 .unwrap();
449
450 let expected_ata = get_associated_token_address(&owner.pubkey(), &mint.pubkey());
452 assert_eq!(ata, expected_ata);
453
454 let account = svm.get_account(&ata).unwrap();
456 assert_eq!(account.owner, spl_token::id());
457
458 let token_data = spl_token::state::Account::unpack(&account.data).unwrap();
460 assert_eq!(token_data.mint, mint.pubkey());
461 assert_eq!(token_data.owner, owner.pubkey());
462 assert_eq!(token_data.amount, 0);
463 }
464
465 #[test]
466 fn test_mint_to() {
467 let mut svm = LiteSVM::new();
468 let authority = svm.create_funded_account(10_000_000_000).unwrap();
469 let mint = svm.create_token_mint(&authority, 9).unwrap();
470 let token_account = svm
471 .create_associated_token_account(&mint.pubkey(), &authority)
472 .unwrap();
473
474 let amount = 1_000_000_000;
475 svm.mint_to(&mint.pubkey(), &token_account, &authority, amount)
476 .unwrap();
477
478 let account = svm.get_account(&token_account).unwrap();
480 let token_data = spl_token::state::Account::unpack(&account.data).unwrap();
481 assert_eq!(token_data.amount, amount);
482
483 let mint_account = svm.get_account(&mint.pubkey()).unwrap();
485 let mint_data = spl_token::state::Mint::unpack(&mint_account.data).unwrap();
486 assert_eq!(mint_data.supply, amount);
487 }
488
489 #[test]
490 fn test_mint_to_multiple_times() {
491 let mut svm = LiteSVM::new();
492 let authority = svm.create_funded_account(10_000_000_000).unwrap();
493 let mint = svm.create_token_mint(&authority, 9).unwrap();
494 let token_account = svm
495 .create_associated_token_account(&mint.pubkey(), &authority)
496 .unwrap();
497
498 svm.mint_to(&mint.pubkey(), &token_account, &authority, 100_000)
500 .unwrap();
501 svm.mint_to(&mint.pubkey(), &token_account, &authority, 200_000)
502 .unwrap();
503 svm.mint_to(&mint.pubkey(), &token_account, &authority, 300_000)
504 .unwrap();
505
506 let account = svm.get_account(&token_account).unwrap();
508 let token_data = spl_token::state::Account::unpack(&account.data).unwrap();
509 assert_eq!(token_data.amount, 600_000);
510 }
511
512 #[test]
513 fn test_derive_pda() {
514 let svm = LiteSVM::new();
515 let program_id = Pubkey::new_unique();
516 let seeds: &[&[u8]] = &[b"test", b"seeds"];
517
518 let (pda, bump) = svm.derive_pda(seeds, &program_id);
519
520 assert!(!pda.is_on_curve());
522
523 let expected_pda =
525 Pubkey::create_program_address(&[seeds[0], seeds[1], &[bump]], &program_id).unwrap();
526 assert_eq!(pda, expected_pda);
527 }
528
529 #[test]
530 fn test_get_pda() {
531 let svm = LiteSVM::new();
532 let program_id = Pubkey::new_unique();
533 let seeds: &[&[u8]] = &[b"vault", b"test"];
534
535 let pda = svm.get_pda(seeds, &program_id);
536
537 let (expected_pda, _) = svm.derive_pda(seeds, &program_id);
539 assert_eq!(pda, expected_pda);
540 }
541
542 #[test]
543 fn test_get_pda_with_bump() {
544 let svm = LiteSVM::new();
545 let program_id = Pubkey::new_unique();
546 let seeds: &[&[u8]] = &[b"escrow"];
547
548 let (pda, bump) = svm.get_pda_with_bump(seeds, &program_id);
549
550 let (expected_pda, expected_bump) = svm.derive_pda(seeds, &program_id);
552 assert_eq!(pda, expected_pda);
553 assert_eq!(bump, expected_bump);
554 }
555
556 #[test]
557 fn test_get_current_slot() {
558 let svm = LiteSVM::new();
559
560 let slot = svm.get_current_slot();
561
562 assert_eq!(slot, 0);
564 }
565
566 #[test]
567 fn test_advance_slot() {
568 let mut svm = LiteSVM::new();
569
570 let initial_slot = svm.get_current_slot();
571 let slots_to_advance = 100;
572
573 svm.advance_slot(slots_to_advance);
574
575 let new_slot = svm.get_current_slot();
576 assert_eq!(new_slot, initial_slot + slots_to_advance);
577 }
578
579 #[test]
580 fn test_advance_slot_multiple_times() {
581 let mut svm = LiteSVM::new();
582
583 svm.advance_slot(10);
584 assert_eq!(svm.get_current_slot(), 10);
585
586 svm.advance_slot(25);
587 assert_eq!(svm.get_current_slot(), 35);
588
589 svm.advance_slot(5);
590 assert_eq!(svm.get_current_slot(), 40);
591 }
592}