@@ -1425,3 +1425,118 @@ async fn success_withdraw_all_fee_tokens() {
14251425 . await ;
14261426 assert_eq ! ( fee_tokens, 0 ) ;
14271427}
1428+
1429+ #[ tokio:: test]
1430+ async fn success_empty_out_stake_with_fee ( ) {
1431+ let (
1432+ mut banks_client,
1433+ payer,
1434+ recent_blockhash,
1435+ stake_pool_accounts,
1436+ _,
1437+ deposit_info,
1438+ user_transfer_authority,
1439+ user_stake_recipient,
1440+ tokens_to_withdraw,
1441+ ) = setup ( ) . await ;
1442+
1443+ // add another validator and deposit into it
1444+ let other_validator_stake_account = simple_add_validator_to_pool (
1445+ & mut banks_client,
1446+ & payer,
1447+ & recent_blockhash,
1448+ & stake_pool_accounts,
1449+ )
1450+ . await ;
1451+
1452+ let other_deposit_info = simple_deposit_stake (
1453+ & mut banks_client,
1454+ & payer,
1455+ & recent_blockhash,
1456+ & stake_pool_accounts,
1457+ & other_validator_stake_account,
1458+ TEST_STAKE_AMOUNT ,
1459+ )
1460+ . await
1461+ . unwrap ( ) ;
1462+
1463+ // move tokens to new account
1464+ transfer_spl_tokens (
1465+ & mut banks_client,
1466+ & payer,
1467+ & recent_blockhash,
1468+ & deposit_info. pool_account . pubkey ( ) ,
1469+ & other_deposit_info. pool_account . pubkey ( ) ,
1470+ & user_transfer_authority,
1471+ tokens_to_withdraw,
1472+ )
1473+ . await ;
1474+
1475+ let user_tokens =
1476+ get_token_balance ( & mut banks_client, & other_deposit_info. pool_account . pubkey ( ) ) . await ;
1477+
1478+ let user_transfer_authority = Keypair :: new ( ) ;
1479+ delegate_tokens (
1480+ & mut banks_client,
1481+ & payer,
1482+ & recent_blockhash,
1483+ & other_deposit_info. pool_account . pubkey ( ) ,
1484+ & other_deposit_info. authority ,
1485+ & user_transfer_authority. pubkey ( ) ,
1486+ user_tokens,
1487+ )
1488+ . await ;
1489+
1490+ // calculate exactly how much to withdraw, given the fee, to get the account
1491+ // down to 0, using an inverse fee calculation
1492+ let validator_stake_account = get_account (
1493+ & mut banks_client,
1494+ & other_validator_stake_account. stake_account ,
1495+ )
1496+ . await ;
1497+ let stake_state =
1498+ deserialize :: < stake:: state:: StakeState > ( & validator_stake_account. data ) . unwrap ( ) ;
1499+ let meta = stake_state. meta ( ) . unwrap ( ) ;
1500+ let lamports_to_withdraw = validator_stake_account. lamports - minimum_stake_lamports ( & meta) ;
1501+ let stake_pool_account =
1502+ get_account ( & mut banks_client, & stake_pool_accounts. stake_pool . pubkey ( ) ) . await ;
1503+ let stake_pool =
1504+ try_from_slice_unchecked :: < state:: StakePool > ( stake_pool_account. data . as_slice ( ) ) . unwrap ( ) ;
1505+ let fee = stake_pool. stake_withdrawal_fee ;
1506+ let inverse_fee = state:: Fee {
1507+ numerator : fee. denominator - fee. numerator ,
1508+ denominator : fee. denominator ,
1509+ } ;
1510+ let pool_tokens_to_withdraw =
1511+ lamports_to_withdraw * inverse_fee. denominator / inverse_fee. numerator ;
1512+
1513+ let new_authority = Pubkey :: new_unique ( ) ;
1514+ let error = stake_pool_accounts
1515+ . withdraw_stake (
1516+ & mut banks_client,
1517+ & payer,
1518+ & recent_blockhash,
1519+ & user_stake_recipient. pubkey ( ) ,
1520+ & user_transfer_authority,
1521+ & other_deposit_info. pool_account . pubkey ( ) ,
1522+ & other_validator_stake_account. stake_account ,
1523+ & new_authority,
1524+ pool_tokens_to_withdraw,
1525+ )
1526+ . await ;
1527+ assert ! ( error. is_none( ) ) ;
1528+
1529+ // Check balance of validator stake account is MINIMUM + rent-exemption
1530+ let validator_stake_account = get_account (
1531+ & mut banks_client,
1532+ & other_validator_stake_account. stake_account ,
1533+ )
1534+ . await ;
1535+ let stake_state =
1536+ deserialize :: < stake:: state:: StakeState > ( & validator_stake_account. data ) . unwrap ( ) ;
1537+ let meta = stake_state. meta ( ) . unwrap ( ) ;
1538+ assert_eq ! (
1539+ validator_stake_account. lamports,
1540+ minimum_stake_lamports( & meta)
1541+ ) ;
1542+ }
0 commit comments