@@ -357,6 +357,47 @@ impl Processor {
357
357
)
358
358
}
359
359
360
+ /// Issue stake_program::withdraw instruction to move additional lamports
361
+ #[ allow( clippy:: too_many_arguments) ]
362
+ fn stake_withdraw < ' a > (
363
+ stake_pool : & Pubkey ,
364
+ source_account : AccountInfo < ' a > ,
365
+ authority : AccountInfo < ' a > ,
366
+ authority_type : & [ u8 ] ,
367
+ bump_seed : u8 ,
368
+ destination_account : AccountInfo < ' a > ,
369
+ clock : AccountInfo < ' a > ,
370
+ stake_history : AccountInfo < ' a > ,
371
+ stake_program_info : AccountInfo < ' a > ,
372
+ lamports : u64 ,
373
+ ) -> Result < ( ) , ProgramError > {
374
+ let me_bytes = stake_pool. to_bytes ( ) ;
375
+ let authority_signature_seeds = [ & me_bytes[ ..32 ] , authority_type, & [ bump_seed] ] ;
376
+ let signers = & [ & authority_signature_seeds[ ..] ] ;
377
+ let custodian_pubkey = None ;
378
+
379
+ let withdraw_instruction = stake_program:: withdraw (
380
+ source_account. key ,
381
+ authority. key ,
382
+ destination_account. key ,
383
+ lamports,
384
+ custodian_pubkey,
385
+ ) ;
386
+
387
+ invoke_signed (
388
+ & withdraw_instruction,
389
+ & [
390
+ source_account,
391
+ destination_account,
392
+ clock,
393
+ stake_history,
394
+ authority,
395
+ stake_program_info,
396
+ ] ,
397
+ signers,
398
+ )
399
+ }
400
+
360
401
/// Issue a spl_token `Burn` instruction.
361
402
#[ allow( clippy:: too_many_arguments) ]
362
403
fn token_burn < ' a > (
@@ -1337,9 +1378,13 @@ impl Processor {
1337
1378
}
1338
1379
}
1339
1380
}
1340
- Some ( stake_program:: StakeState :: Stake ( _, stake) ) => {
1381
+ Some ( stake_program:: StakeState :: Stake ( meta, stake) ) => {
1382
+ let account_stake = meta
1383
+ . rent_exempt_reserve
1384
+ . checked_add ( stake. delegation . stake )
1385
+ . ok_or ( StakePoolError :: CalculationFailure ) ?;
1341
1386
if no_merge {
1342
- transient_stake_lamports = transient_stake_info . lamports ( ) ;
1387
+ transient_stake_lamports = account_stake ;
1343
1388
} else if stake. delegation . deactivation_epoch < clock. epoch {
1344
1389
// deactivated, merge into reserve
1345
1390
Self :: stake_merge (
@@ -1378,15 +1423,15 @@ impl Processor {
1378
1423
) ?;
1379
1424
} else {
1380
1425
msg ! ( "Stake activating or just active, not ready to merge" ) ;
1381
- transient_stake_lamports = transient_stake_info . lamports ( ) ;
1426
+ transient_stake_lamports = account_stake ;
1382
1427
}
1383
1428
} else {
1384
1429
msg ! ( "Transient stake is activating or active, but validator stake is not, need to add the validator stake account on {} back into the stake pool" , stake. delegation. voter_pubkey) ;
1385
- transient_stake_lamports = transient_stake_info . lamports ( ) ;
1430
+ transient_stake_lamports = account_stake ;
1386
1431
}
1387
1432
} else {
1388
1433
msg ! ( "Transient stake not ready to be merged anywhere" ) ;
1389
- transient_stake_lamports = transient_stake_info . lamports ( ) ;
1434
+ transient_stake_lamports = account_stake ;
1390
1435
}
1391
1436
}
1392
1437
None
@@ -1398,11 +1443,13 @@ impl Processor {
1398
1443
// * active -> do everything
1399
1444
// * any other state / not a stake -> error state, but account for transient stake
1400
1445
match validator_stake_state {
1401
- Some ( stake_program:: StakeState :: Stake ( meta , _ ) ) => {
1446
+ Some ( stake_program:: StakeState :: Stake ( _ , stake ) ) => {
1402
1447
if validator_stake_record. status == StakeStatus :: Active {
1403
- active_stake_lamports = validator_stake_info
1404
- . lamports ( )
1405
- . saturating_sub ( minimum_stake_lamports ( & meta) ) ;
1448
+ active_stake_lamports = stake
1449
+ . delegation
1450
+ . stake
1451
+ . checked_sub ( MINIMUM_ACTIVE_STAKE )
1452
+ . ok_or ( StakePoolError :: CalculationFailure ) ?;
1406
1453
} else {
1407
1454
msg ! ( "Validator stake account no longer part of the pool, ignoring" ) ;
1408
1455
}
@@ -1566,6 +1613,7 @@ impl Processor {
1566
1613
let withdraw_authority_info = next_account_info ( account_info_iter) ?;
1567
1614
let stake_info = next_account_info ( account_info_iter) ?;
1568
1615
let validator_stake_account_info = next_account_info ( account_info_iter) ?;
1616
+ let reserve_stake_account_info = next_account_info ( account_info_iter) ?;
1569
1617
let dest_user_info = next_account_info ( account_info_iter) ?;
1570
1618
let pool_mint_info = next_account_info ( account_info_iter) ?;
1571
1619
let clock_info = next_account_info ( account_info_iter) ?;
@@ -1594,6 +1642,7 @@ impl Processor {
1594
1642
stake_pool. check_deposit_authority ( deposit_authority_info. key ) ?;
1595
1643
stake_pool. check_mint ( pool_mint_info) ?;
1596
1644
stake_pool. check_validator_list ( validator_list_info) ?;
1645
+ stake_pool. check_reserve_stake ( reserve_stake_account_info) ?;
1597
1646
1598
1647
if stake_pool. token_program_id != * token_program_info. key {
1599
1648
return Err ( ProgramError :: IncorrectProgramId ) ;
@@ -1610,8 +1659,9 @@ impl Processor {
1610
1659
return Err ( StakePoolError :: InvalidState . into ( ) ) ;
1611
1660
}
1612
1661
1613
- let ( meta, stake) = get_stake_state ( validator_stake_account_info) ?;
1614
- let vote_account_address = stake. delegation . voter_pubkey ;
1662
+ let ( _, validator_stake) = get_stake_state ( validator_stake_account_info) ?;
1663
+ let pre_all_validator_lamports = validator_stake_account_info. lamports ( ) ;
1664
+ let vote_account_address = validator_stake. delegation . voter_pubkey ;
1615
1665
check_validator_stake_address (
1616
1666
program_id,
1617
1667
stake_pool_info. key ,
@@ -1633,15 +1683,7 @@ impl Processor {
1633
1683
return Err ( StakePoolError :: ValidatorNotFound . into ( ) ) ;
1634
1684
}
1635
1685
1636
- let stake_lamports = * * stake_info. lamports . borrow ( ) ;
1637
- let new_pool_tokens = stake_pool
1638
- . calc_pool_tokens_for_deposit ( stake_lamports)
1639
- . ok_or ( StakePoolError :: CalculationFailure ) ?;
1640
-
1641
- msg ! (
1642
- "lamports pre merge {}" ,
1643
- validator_stake_account_info. lamports( )
1644
- ) ;
1686
+ msg ! ( "Stake pre merge {}" , validator_stake. delegation. stake) ;
1645
1687
1646
1688
let ( deposit_authority_program_address, deposit_bump_seed) =
1647
1689
find_deposit_authority_program_address ( program_id, stake_pool_info. key ) ;
@@ -1678,6 +1720,21 @@ impl Processor {
1678
1720
stake_program_info. clone ( ) ,
1679
1721
) ?;
1680
1722
1723
+ let ( _, post_validator_stake) = get_stake_state ( validator_stake_account_info) ?;
1724
+ let post_all_validator_lamports = validator_stake_account_info. lamports ( ) ;
1725
+ msg ! ( "Stake post merge {}" , post_validator_stake. delegation. stake) ;
1726
+ let all_deposit_lamports = post_all_validator_lamports
1727
+ . checked_sub ( pre_all_validator_lamports)
1728
+ . ok_or ( StakePoolError :: CalculationFailure ) ?;
1729
+ let stake_deposit_lamports = post_validator_stake
1730
+ . delegation
1731
+ . stake
1732
+ . checked_sub ( validator_stake. delegation . stake )
1733
+ . ok_or ( StakePoolError :: CalculationFailure ) ?;
1734
+ let new_pool_tokens = stake_pool
1735
+ . calc_pool_tokens_for_deposit ( all_deposit_lamports)
1736
+ . ok_or ( StakePoolError :: CalculationFailure ) ?;
1737
+
1681
1738
Self :: token_mint_to (
1682
1739
stake_pool_info. key ,
1683
1740
token_program_info. clone ( ) ,
@@ -1689,23 +1746,39 @@ impl Processor {
1689
1746
new_pool_tokens,
1690
1747
) ?;
1691
1748
1749
+ // withdraw additional lamports to the reserve
1750
+ let additional_lamports = all_deposit_lamports
1751
+ . checked_sub ( stake_deposit_lamports)
1752
+ . ok_or ( StakePoolError :: CalculationFailure ) ?;
1753
+ if additional_lamports > 0 {
1754
+ Self :: stake_withdraw (
1755
+ stake_pool_info. key ,
1756
+ validator_stake_account_info. clone ( ) ,
1757
+ withdraw_authority_info. clone ( ) ,
1758
+ AUTHORITY_WITHDRAW ,
1759
+ stake_pool. withdraw_bump_seed ,
1760
+ reserve_stake_account_info. clone ( ) ,
1761
+ clock_info. clone ( ) ,
1762
+ stake_history_info. clone ( ) ,
1763
+ stake_program_info. clone ( ) ,
1764
+ additional_lamports,
1765
+ ) ?;
1766
+ }
1767
+
1692
1768
stake_pool. pool_token_supply = stake_pool
1693
1769
. pool_token_supply
1694
1770
. checked_add ( new_pool_tokens)
1695
1771
. ok_or ( StakePoolError :: CalculationFailure ) ?;
1696
1772
stake_pool. total_stake_lamports = stake_pool
1697
1773
. total_stake_lamports
1698
- . checked_add ( stake_lamports )
1774
+ . checked_add ( all_deposit_lamports )
1699
1775
. ok_or ( StakePoolError :: CalculationFailure ) ?;
1700
1776
stake_pool. serialize ( & mut * stake_pool_info. data . borrow_mut ( ) ) ?;
1701
1777
1702
- msg ! (
1703
- "lamports post merge {}" ,
1704
- validator_stake_account_info. lamports( )
1705
- ) ;
1706
- validator_list_item. active_stake_lamports = validator_stake_account_info
1707
- . lamports ( )
1708
- . checked_sub ( minimum_stake_lamports ( & meta) )
1778
+ validator_list_item. active_stake_lamports = post_validator_stake
1779
+ . delegation
1780
+ . stake
1781
+ . checked_sub ( MINIMUM_ACTIVE_STAKE )
1709
1782
. ok_or ( StakePoolError :: CalculationFailure ) ?;
1710
1783
validator_list. serialize ( & mut * validator_list_info. data . borrow_mut ( ) ) ?;
1711
1784
@@ -1794,7 +1867,7 @@ impl Processor {
1794
1867
. ok_or ( StakePoolError :: StakeLamportsNotEqualToMinimum ) ?;
1795
1868
None
1796
1869
} else {
1797
- let ( meta , stake) = get_stake_state ( stake_split_from) ?;
1870
+ let ( _ , stake) = get_stake_state ( stake_split_from) ?;
1798
1871
let vote_account_address = stake. delegation . voter_pubkey ;
1799
1872
1800
1873
if let Some ( preferred_withdraw_validator) =
@@ -1840,11 +1913,9 @@ impl Processor {
1840
1913
return Err ( StakePoolError :: ValidatorNotFound . into ( ) ) ;
1841
1914
}
1842
1915
1843
- let required_lamports = minimum_stake_lamports ( & meta) ;
1844
- let current_lamports = stake_split_from. lamports ( ) ;
1845
- let remaining_lamports = current_lamports. saturating_sub ( withdraw_lamports) ;
1846
- if remaining_lamports < required_lamports {
1847
- msg ! ( "Attempting to withdraw {} lamports from validator account with {} lamports, {} must remain" , withdraw_lamports, current_lamports, required_lamports) ;
1916
+ let remaining_lamports = stake. delegation . stake . saturating_sub ( withdraw_lamports) ;
1917
+ if remaining_lamports < MINIMUM_ACTIVE_STAKE {
1918
+ msg ! ( "Attempting to withdraw {} lamports from validator account with {} stake lamports, {} must remain" , withdraw_lamports, stake. delegation. stake, MINIMUM_ACTIVE_STAKE ) ;
1848
1919
return Err ( StakePoolError :: StakeLamportsNotEqualToMinimum . into ( ) ) ;
1849
1920
}
1850
1921
Some ( ( validator_list_item, withdrawing_from_transient_stake) )
0 commit comments