diff --git a/CHANGELOG.md b/CHANGELOG.md index aae3bafe..53d3c743 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to ## Changed +- Stake: unbonding now automatically pays the rewards ([#170]) - Update soroban-sdk version from v20.0.3 to v20.1.0 ([#193]) - Fixes documentation and naming ([#200]) - Multihop: adds a new field in the Swap struct, that hold max_belief_price ([234]) @@ -19,6 +20,7 @@ and this project adheres to - Factory, Multihop, Pool, Pool_stable, Phoenix: adds lp_token decimal's as a const instead a user input ([#241]) - Factory, Multihop, Pool, Pool_stable: adds a new parameter for creating liquidity pool ([#243]) +[#170]: https://github.com/Phoenix-Protocol-Group/phoenix-contracts/pull/170 [#200]: https://github.com/Phoenix-Protocol-Group/phoenix-contracts/pull/200 [#234]: https://github.com/Phoenix-Protocol-Group/phoenix-contracts/pull/234 [#235]: https://github.com/Phoenix-Protocol-Group/phoenix-contracts/pull/235 diff --git a/contracts/stake/src/contract.rs b/contracts/stake/src/contract.rs index c6f357db..1af9401e 100644 --- a/contracts/stake/src/contract.rs +++ b/contracts/stake/src/contract.rs @@ -181,6 +181,14 @@ impl StakingTrait for Staking { let config = get_config(&env); + // check for rewards and withdraw them + let found_rewards: WithdrawableRewardsResponse = + Self::query_withdrawable_rewards(env.clone(), sender.clone()); + + if !found_rewards.rewards.is_empty() { + Self::withdraw_rewards(env.clone(), sender.clone()); + } + let mut stakes = get_stakes(&env, &sender); remove_stake(&mut stakes.stakes, stake_amount, stake_timestamp); stakes.total_stake -= stake_amount as u128; diff --git a/contracts/stake/src/tests/bond.rs b/contracts/stake/src/tests/bond.rs index 0ad020fe..052b56e2 100644 --- a/contracts/stake/src/tests/bond.rs +++ b/contracts/stake/src/tests/bond.rs @@ -226,3 +226,53 @@ fn unbond_wrong_user_stake_not_found() { staking.unbond(&user2, &10_000, &2_000); } + +#[test] +fn pay_rewards_during_unbond() { + const STAKED_AMOUNT: i128 = 1_000; + let env = Env::default(); + env.mock_all_auths(); + + let admin = Address::generate(&env); + let user = Address::generate(&env); + let manager = Address::generate(&env); + let owner = Address::generate(&env); + + let lp_token = deploy_token_contract(&env, &admin); + let reward_token = deploy_token_contract(&env, &admin); + let staking = deploy_staking_contract(&env, admin.clone(), &lp_token.address, &manager, &owner); + + lp_token.mint(&user, &10_000); + reward_token.mint(&admin, &10_000); + + staking.create_distribution_flow(&manager, &reward_token.address); + staking.fund_distribution(&admin, &0u64, &10_000u64, &reward_token.address, &10_000); + + staking.bond(&user, &STAKED_AMOUNT); + + env.ledger().with_mut(|li| { + li.timestamp = 5_000; + }); + staking.distribute_rewards(); + + // user has bonded for 5_000 time, initial rewards are 10_000 + // so user should have 5_000 rewards + // 5_000 rewards are still undistributed + assert_eq!( + staking.query_undistributed_rewards(&reward_token.address), + 5_000 + ); + assert_eq!( + staking + .query_withdrawable_rewards(&user) + .rewards + .iter() + .map(|reward| reward.reward_amount) + .sum::(), + 5_000 + ); + + assert_eq!(reward_token.balance(&user), 0); + staking.unbond(&user, &STAKED_AMOUNT, &0); + assert_eq!(reward_token.balance(&user), 5_000); +}