@@ -49,7 +49,7 @@ use sp_runtime::{
4949} ;
5050use sp_staking:: {
5151 offence:: { OffenceDetails , OnOffenceHandler } ,
52- SessionIndex , Stake , StakingInterface ,
52+ SessionIndex , StakingInterface ,
5353} ;
5454use substrate_test_utils:: assert_eq_uvec;
5555
@@ -5099,72 +5099,6 @@ fn restricted_accounts_can_only_withdraw() {
50995099 } )
51005100}
51015101
5102- #[ test]
5103- fn permissionless_withdraw_overstake ( ) {
5104- ExtBuilder :: default ( ) . build_and_execute ( || {
5105- // Given Alice, Bob and Charlie with some stake.
5106- let alice = 301 ;
5107- let bob = 302 ;
5108- let charlie = 303 ;
5109- let _ = Balances :: make_free_balance_be ( & alice, 500 ) ;
5110- let _ = Balances :: make_free_balance_be ( & bob, 500 ) ;
5111- let _ = Balances :: make_free_balance_be ( & charlie, 500 ) ;
5112- assert_ok ! ( Staking :: bond( RuntimeOrigin :: signed( alice) , 100 , RewardDestination :: Staked ) ) ;
5113- assert_ok ! ( Staking :: bond( RuntimeOrigin :: signed( bob) , 100 , RewardDestination :: Staked ) ) ;
5114- assert_ok ! ( Staking :: bond( RuntimeOrigin :: signed( charlie) , 100 , RewardDestination :: Staked ) ) ;
5115-
5116- // WHEN: charlie is partially unbonding.
5117- assert_ok ! ( Staking :: unbond( RuntimeOrigin :: signed( charlie) , 90 ) ) ;
5118- let charlie_ledger = StakingLedger :: < Test > :: get ( StakingAccount :: Stash ( charlie) ) . unwrap ( ) ;
5119-
5120- // AND: alice and charlie ledger having higher value than actual stake.
5121- Ledger :: < Test > :: insert ( alice, StakingLedger :: < Test > :: new ( alice, 200 ) ) ;
5122- Ledger :: < Test > :: insert (
5123- charlie,
5124- StakingLedger { stash : charlie, total : 200 , active : 200 - 90 , ..charlie_ledger } ,
5125- ) ;
5126-
5127- // THEN overstake can be permissionlessly withdrawn.
5128- System :: reset_events ( ) ;
5129-
5130- // Alice stake is corrected.
5131- assert_eq ! (
5132- <Staking as StakingInterface >:: stake( & alice) . unwrap( ) ,
5133- Stake { total: 200 , active: 200 }
5134- ) ;
5135- assert_ok ! ( Staking :: withdraw_overstake( RuntimeOrigin :: signed( 1 ) , alice) ) ;
5136- assert_eq ! (
5137- <Staking as StakingInterface >:: stake( & alice) . unwrap( ) ,
5138- Stake { total: 100 , active: 100 }
5139- ) ;
5140-
5141- // Charlie who is partially withdrawing also gets their stake corrected.
5142- assert_eq ! (
5143- <Staking as StakingInterface >:: stake( & charlie) . unwrap( ) ,
5144- Stake { total: 200 , active: 110 }
5145- ) ;
5146- assert_ok ! ( Staking :: withdraw_overstake( RuntimeOrigin :: signed( 1 ) , charlie) ) ;
5147- assert_eq ! (
5148- <Staking as StakingInterface >:: stake( & charlie) . unwrap( ) ,
5149- Stake { total: 200 - 100 , active: 110 - 100 }
5150- ) ;
5151-
5152- assert_eq ! (
5153- staking_events_since_last_call( ) ,
5154- vec![
5155- Event :: Withdrawn { stash: alice, amount: 200 - 100 } ,
5156- Event :: Withdrawn { stash: charlie, amount: 200 - 100 }
5157- ]
5158- ) ;
5159-
5160- // but Bob ledger is fine and that cannot be withdrawn.
5161- assert_noop ! (
5162- Staking :: withdraw_overstake( RuntimeOrigin :: signed( 1 ) , bob) ,
5163- Error :: <Test >:: BoundNotMet
5164- ) ;
5165- } ) ;
5166- }
5167-
51685102mod election_data_provider {
51695103 use super :: * ;
51705104 use frame_election_provider_support:: ElectionDataProvider ;
@@ -9071,6 +9005,92 @@ mod hold_migration {
90719005 } ) ;
90729006 }
90739007
9008+ #[ test]
9009+ fn overstaked_and_partially_unbonding ( ) {
9010+ ExtBuilder :: default ( ) . has_stakers ( true ) . build_and_execute ( || {
9011+ // GIVEN alice who is a nominator with T::OldCurrency.
9012+ let alice = 300 ;
9013+ // 1000 + ED
9014+ let _ = Balances :: make_free_balance_be ( & alice, 1001 ) ;
9015+ let stake = 600 ;
9016+ let reserved_by_another_pallet = 400 ;
9017+ assert_ok ! ( Staking :: bond(
9018+ RuntimeOrigin :: signed( alice) ,
9019+ stake,
9020+ RewardDestination :: Staked
9021+ ) ) ;
9022+
9023+ // AND Alice is partially unbonding.
9024+ assert_ok ! ( Staking :: unbond( RuntimeOrigin :: signed( alice) , 300 ) ) ;
9025+
9026+ // AND Alice has some funds reserved with another pallet.
9027+ assert_ok ! ( Balances :: reserve( & alice, reserved_by_another_pallet) ) ;
9028+
9029+ // convert stake to T::OldCurrency.
9030+ testing_utils:: migrate_to_old_currency :: < Test > ( alice) ;
9031+ assert_eq ! ( asset:: staked:: <Test >( & alice) , 0 ) ;
9032+ assert_eq ! ( Balances :: balance_locked( STAKING_ID , & alice) , stake) ;
9033+
9034+ // ledger has correct amount staked.
9035+ assert_eq ! (
9036+ <Staking as StakingInterface >:: stake( & alice) ,
9037+ Ok ( Stake { total: stake, active: stake - 300 } )
9038+ ) ;
9039+
9040+ // Alice becomes overstaked by withdrawing some staked balance.
9041+ assert_ok ! ( Balances :: transfer_allow_death(
9042+ RuntimeOrigin :: signed( alice) ,
9043+ 10 ,
9044+ reserved_by_another_pallet
9045+ ) ) ;
9046+
9047+ let expected_force_withdraw = reserved_by_another_pallet;
9048+
9049+ // ledger mutation would fail in this case before migration because of failing hold.
9050+ assert_noop ! (
9051+ Staking :: unbond( RuntimeOrigin :: signed( alice) , 100 ) ,
9052+ Error :: <Test >:: NotEnoughFunds
9053+ ) ;
9054+
9055+ // clear events
9056+ System :: reset_events ( ) ;
9057+
9058+ // WHEN alice currency is migrated.
9059+ assert_ok ! ( Staking :: migrate_currency( RuntimeOrigin :: signed( 1 ) , alice) ) ;
9060+
9061+ // THEN
9062+ let expected_hold = stake - expected_force_withdraw;
9063+ // ensure no lock
9064+ assert_eq ! ( Balances :: balance_locked( STAKING_ID , & alice) , 0 ) ;
9065+ // ensure stake and hold are same.
9066+ assert_eq ! (
9067+ <Staking as StakingInterface >:: stake( & alice) ,
9068+ // expected stake is 0 since force withdrawn (400) is taken out completely of
9069+ // active stake.
9070+ Ok ( Stake { total: expected_hold, active: 0 } )
9071+ ) ;
9072+
9073+ assert_eq ! ( asset:: staked:: <Test >( & alice) , expected_hold) ;
9074+ // ensure events are emitted.
9075+ assert_eq ! (
9076+ staking_events_since_last_call( ) ,
9077+ vec![ Event :: CurrencyMigrated {
9078+ stash: alice,
9079+ force_withdraw: expected_force_withdraw
9080+ } ]
9081+ ) ;
9082+
9083+ // ensure cannot migrate again.
9084+ assert_noop ! (
9085+ Staking :: migrate_currency( RuntimeOrigin :: signed( 1 ) , alice) ,
9086+ Error :: <Test >:: AlreadyMigrated
9087+ ) ;
9088+
9089+ // unbond works after migration.
9090+ assert_ok ! ( Staking :: unbond( RuntimeOrigin :: signed( alice) , 100 ) ) ;
9091+ } ) ;
9092+ }
9093+
90749094 #[ test]
90759095 fn virtual_staker_consumer_provider_dec ( ) {
90769096 // Ensure virtual stakers consumer and provider count is decremented.
0 commit comments