@@ -254,34 +254,50 @@ impl Processor {
254
254
let authority_info = next_account_info ( account_info_iter) ?;
255
255
256
256
if account_info. data_len ( ) == size_of :: < Account > ( ) {
257
- if authority_type != AuthorityType :: AccountHolder {
258
- return Err ( TokenError :: AuthorityTypeNotSupported . into ( ) ) ;
259
- }
260
257
let mut account_data = account_info. data . borrow_mut ( ) ;
261
258
let mut account: & mut Account = state:: unpack ( & mut account_data) ?;
262
259
263
260
if account. is_frozen ( ) {
264
261
return Err ( TokenError :: AccountFrozen . into ( ) ) ;
265
262
}
266
263
267
- Self :: validate_owner (
268
- program_id,
269
- & account. owner ,
270
- authority_info,
271
- account_info_iter. as_slice ( ) ,
272
- ) ?;
264
+ match authority_type {
265
+ AuthorityType :: AccountHolder => {
266
+ Self :: validate_owner (
267
+ program_id,
268
+ & account. owner ,
269
+ authority_info,
270
+ account_info_iter. as_slice ( ) ,
271
+ ) ?;
273
272
274
- if let COption :: Some ( authority) = new_authority {
275
- account. owner = authority;
276
- } else {
277
- return Err ( TokenError :: InvalidInstruction . into ( ) ) ;
273
+ if let COption :: Some ( authority) = new_authority {
274
+ account. owner = authority;
275
+ } else {
276
+ return Err ( TokenError :: InvalidInstruction . into ( ) ) ;
277
+ }
278
+ }
279
+ AuthorityType :: CloseAccount => {
280
+ let authority = account. close_authority . unwrap_or ( account. owner ) ;
281
+ Self :: validate_owner (
282
+ program_id,
283
+ & authority,
284
+ authority_info,
285
+ account_info_iter. as_slice ( ) ,
286
+ ) ?;
287
+ account. close_authority = new_authority;
288
+ }
289
+ _ => {
290
+ return Err ( TokenError :: AuthorityTypeNotSupported . into ( ) ) ;
291
+ }
278
292
}
279
293
} else if account_info. data_len ( ) == size_of :: < Mint > ( ) {
280
294
let mut account_data = account_info. data . borrow_mut ( ) ;
281
295
let mut mint: & mut Mint = state:: unpack ( & mut account_data) ?;
282
296
283
297
match authority_type {
284
298
AuthorityType :: MintTokens => {
299
+ // Once a mint's supply is fixed, it cannot be undone by setting a new
300
+ // mint_authority
285
301
let mint_authority = mint
286
302
. mint_authority
287
303
. ok_or ( Into :: < ProgramError > :: into ( TokenError :: FixedSupply ) ) ?;
@@ -294,6 +310,8 @@ impl Processor {
294
310
mint. mint_authority = new_authority;
295
311
}
296
312
AuthorityType :: FreezeAccount => {
313
+ // Once a mint's freeze authority is disabled, it cannot be re-enabled by
314
+ // setting a new freeze_authority
297
315
let freeze_authority = mint
298
316
. freeze_authority
299
317
. ok_or ( Into :: < ProgramError > :: into ( TokenError :: MintCannotFreeze ) ) ?;
@@ -429,13 +447,16 @@ impl Processor {
429
447
let mut source_data = source_account_info. data . borrow_mut ( ) ;
430
448
let source_account: & mut Account = state:: unpack ( & mut source_data) ?;
431
449
432
- if !source_account. is_native {
433
- return Err ( TokenError :: NonNativeNotSupported . into ( ) ) ;
450
+ if !source_account. is_native && source_account . amount != 0 {
451
+ return Err ( TokenError :: NonNativeHasBalance . into ( ) ) ;
434
452
}
435
453
454
+ let authority = source_account
455
+ . close_authority
456
+ . unwrap_or ( source_account. owner ) ;
436
457
Self :: validate_owner (
437
458
program_id,
438
- & source_account . owner ,
459
+ & authority ,
439
460
authority_info,
440
461
account_info_iter. as_slice ( ) ,
441
462
) ?;
@@ -626,8 +647,8 @@ impl PrintProgramError for TokenError {
626
647
TokenError :: NativeNotSupported => {
627
648
info ! ( "Error: Instruction does not support native tokens" )
628
649
}
629
- TokenError :: NonNativeNotSupported => {
630
- info ! ( "Error: Instruction does not support non-native tokens " )
650
+ TokenError :: NonNativeHasBalance => {
651
+ info ! ( "Error: Non-native account can only be closed if its balance is zero " )
631
652
}
632
653
TokenError :: InvalidInstruction => info ! ( "Error: Invalid instruction" ) ,
633
654
TokenError :: InvalidState => info ! ( "Error: Invalid account state for operation" ) ,
@@ -1444,7 +1465,7 @@ mod tests {
1444
1465
& [ ]
1445
1466
)
1446
1467
. unwrap( ) ,
1447
- vec![ & mut account_account, & mut owner_account, ] ,
1468
+ vec![ & mut account_account, & mut owner_account] ,
1448
1469
)
1449
1470
) ;
1450
1471
@@ -1479,7 +1500,7 @@ mod tests {
1479
1500
& [ ]
1480
1501
)
1481
1502
. unwrap( ) ,
1482
- vec![ & mut account_account, & mut owner2_account, ] ,
1503
+ vec![ & mut account_account, & mut owner2_account] ,
1483
1504
)
1484
1505
) ;
1485
1506
@@ -1512,7 +1533,24 @@ mod tests {
1512
1533
& [ ] ,
1513
1534
)
1514
1535
. unwrap( ) ,
1515
- vec![ & mut account_account, & mut owner_account, ] ,
1536
+ vec![ & mut account_account, & mut owner_account] ,
1537
+ )
1538
+ ) ;
1539
+
1540
+ // account owner may not be set to None
1541
+ assert_eq ! (
1542
+ Err ( TokenError :: InvalidInstruction . into( ) ) ,
1543
+ do_process_instruction(
1544
+ set_authority(
1545
+ & program_id,
1546
+ & account_key,
1547
+ None ,
1548
+ AuthorityType :: AccountHolder ,
1549
+ & owner_key,
1550
+ & [ ] ,
1551
+ )
1552
+ . unwrap( ) ,
1553
+ vec![ & mut account_account, & mut owner_account] ,
1516
1554
)
1517
1555
) ;
1518
1556
@@ -1531,6 +1569,36 @@ mod tests {
1531
1569
)
1532
1570
. unwrap ( ) ;
1533
1571
1572
+ // set close_authority
1573
+ do_process_instruction (
1574
+ set_authority (
1575
+ & program_id,
1576
+ & account_key,
1577
+ Some ( & owner2_key) ,
1578
+ AuthorityType :: CloseAccount ,
1579
+ & owner2_key,
1580
+ & [ ] ,
1581
+ )
1582
+ . unwrap ( ) ,
1583
+ vec ! [ & mut account_account, & mut owner2_account] ,
1584
+ )
1585
+ . unwrap ( ) ;
1586
+
1587
+ // close_authority may be set to None
1588
+ do_process_instruction (
1589
+ set_authority (
1590
+ & program_id,
1591
+ & account_key,
1592
+ None ,
1593
+ AuthorityType :: CloseAccount ,
1594
+ & owner2_key,
1595
+ & [ ] ,
1596
+ )
1597
+ . unwrap ( ) ,
1598
+ vec ! [ & mut account_account, & mut owner2_account] ,
1599
+ )
1600
+ . unwrap ( ) ;
1601
+
1534
1602
// create new mint with owner
1535
1603
do_process_instruction (
1536
1604
initialize_mint (
@@ -2486,12 +2554,28 @@ mod tests {
2486
2554
)
2487
2555
) ;
2488
2556
2489
- // initialize non-native account
2557
+ // initialize and mint to non-native account
2490
2558
do_process_instruction (
2491
2559
initialize_account ( & program_id, & account_key, & mint_key, & owner_key) . unwrap ( ) ,
2492
2560
vec ! [ & mut account_account, & mut mint_account, & mut owner_account] ,
2493
2561
)
2494
2562
. unwrap ( ) ;
2563
+ do_process_instruction (
2564
+ initialize_mint (
2565
+ & program_id,
2566
+ & mint_key,
2567
+ Some ( & account_key) ,
2568
+ None ,
2569
+ None ,
2570
+ 42 ,
2571
+ 2 ,
2572
+ )
2573
+ . unwrap ( ) ,
2574
+ vec ! [ & mut mint_account, & mut account_account, & mut owner_account] ,
2575
+ )
2576
+ . unwrap ( ) ;
2577
+ let account: & mut Account = state:: unpack ( & mut account_account. data ) . unwrap ( ) ;
2578
+ assert_eq ! ( account. amount, 42 ) ;
2495
2579
2496
2580
// initialize native account
2497
2581
do_process_instruction (
@@ -2509,9 +2593,9 @@ mod tests {
2509
2593
assert ! ( account. is_native) ;
2510
2594
assert_eq ! ( account. amount, 2 ) ;
2511
2595
2512
- // close non-native account
2596
+ // close non-native account with balance
2513
2597
assert_eq ! (
2514
- Err ( TokenError :: NonNativeNotSupported . into( ) ) ,
2598
+ Err ( TokenError :: NonNativeHasBalance . into( ) ) ,
2515
2599
do_process_instruction(
2516
2600
close_account( & program_id, & account_key, & account3_key, & owner_key, & [ ] ) . unwrap( ) ,
2517
2601
vec![
@@ -2523,6 +2607,94 @@ mod tests {
2523
2607
) ;
2524
2608
assert_eq ! ( account_account. lamports, 42 ) ;
2525
2609
2610
+ // empty account
2611
+ do_process_instruction (
2612
+ burn ( & program_id, & account_key, & owner_key, & [ ] , 42 ) . unwrap ( ) ,
2613
+ vec ! [ & mut account_account, & mut owner_account] ,
2614
+ )
2615
+ . unwrap ( ) ;
2616
+
2617
+ // wrong owner
2618
+ assert_eq ! (
2619
+ Err ( TokenError :: OwnerMismatch . into( ) ) ,
2620
+ do_process_instruction(
2621
+ close_account( & program_id, & account_key, & account3_key, & owner2_key, & [ ] ) . unwrap( ) ,
2622
+ vec![
2623
+ & mut account_account,
2624
+ & mut account3_account,
2625
+ & mut owner2_account,
2626
+ ] ,
2627
+ )
2628
+ ) ;
2629
+
2630
+ // close account
2631
+ do_process_instruction (
2632
+ close_account ( & program_id, & account_key, & account3_key, & owner_key, & [ ] ) . unwrap ( ) ,
2633
+ vec ! [
2634
+ & mut account_account,
2635
+ & mut account3_account,
2636
+ & mut owner_account,
2637
+ ] ,
2638
+ )
2639
+ . unwrap ( ) ;
2640
+ let account: & mut Account = state:: unpack_unchecked ( & mut account_account. data ) . unwrap ( ) ;
2641
+ assert_eq ! ( account_account. lamports, 0 ) ;
2642
+ assert_eq ! ( account. amount, 0 ) ;
2643
+ assert_eq ! ( account3_account. lamports, 44 ) ;
2644
+
2645
+ // fund and initialize new non-native account to test close authority
2646
+ let account_key = pubkey_rand ( ) ;
2647
+ let mut account_account = SolanaAccount :: new ( 42 , size_of :: < Account > ( ) , & program_id) ;
2648
+ let owner2_key = pubkey_rand ( ) ;
2649
+ let mut owner2_account = SolanaAccount :: new ( 42 , size_of :: < Account > ( ) , & program_id) ;
2650
+ do_process_instruction (
2651
+ initialize_account ( & program_id, & account_key, & mint_key, & owner_key) . unwrap ( ) ,
2652
+ vec ! [ & mut account_account, & mut mint_account, & mut owner_account] ,
2653
+ )
2654
+ . unwrap ( ) ;
2655
+ account_account. lamports = 2 ;
2656
+
2657
+ do_process_instruction (
2658
+ set_authority (
2659
+ & program_id,
2660
+ & account_key,
2661
+ Some ( & owner2_key) ,
2662
+ AuthorityType :: CloseAccount ,
2663
+ & owner_key,
2664
+ & [ ] ,
2665
+ )
2666
+ . unwrap ( ) ,
2667
+ vec ! [ & mut account_account, & mut owner_account] ,
2668
+ )
2669
+ . unwrap ( ) ;
2670
+
2671
+ // account owner cannot authorize close if close_authority is set
2672
+ assert_eq ! (
2673
+ Err ( TokenError :: OwnerMismatch . into( ) ) ,
2674
+ do_process_instruction(
2675
+ close_account( & program_id, & account_key, & account3_key, & owner_key, & [ ] ) . unwrap( ) ,
2676
+ vec![
2677
+ & mut account_account,
2678
+ & mut account3_account,
2679
+ & mut owner_account,
2680
+ ] ,
2681
+ )
2682
+ ) ;
2683
+
2684
+ // close non-native account with close_authority
2685
+ do_process_instruction (
2686
+ close_account ( & program_id, & account_key, & account3_key, & owner2_key, & [ ] ) . unwrap ( ) ,
2687
+ vec ! [
2688
+ & mut account_account,
2689
+ & mut account3_account,
2690
+ & mut owner2_account,
2691
+ ] ,
2692
+ )
2693
+ . unwrap ( ) ;
2694
+ assert_eq ! ( account_account. lamports, 0 ) ;
2695
+ assert_eq ! ( account. amount, 0 ) ;
2696
+ assert_eq ! ( account3_account. lamports, 46 ) ;
2697
+
2526
2698
// close native account
2527
2699
do_process_instruction (
2528
2700
close_account ( & program_id, & account2_key, & account3_key, & owner_key, & [ ] ) . unwrap ( ) ,
@@ -2535,8 +2707,9 @@ mod tests {
2535
2707
. unwrap ( ) ;
2536
2708
let account: & mut Account = state:: unpack_unchecked ( & mut account2_account. data ) . unwrap ( ) ;
2537
2709
assert ! ( account. is_native) ;
2710
+ assert_eq ! ( account_account. lamports, 0 ) ;
2538
2711
assert_eq ! ( account. amount, 0 ) ;
2539
- assert_eq ! ( account3_account. lamports, 4 ) ;
2712
+ assert_eq ! ( account3_account. lamports, 48 ) ;
2540
2713
}
2541
2714
2542
2715
#[ test]
0 commit comments