From 88dd12bb4fe8184729545f95e4659602dc067819 Mon Sep 17 00:00:00 2001 From: Sorin Petreasca Date: Fri, 2 Aug 2024 14:10:50 +0300 Subject: [PATCH 1/4] farm with locked rewards unit tests --- .../farm_with_locked_rewards_setup/mod.rs | 87 +++++++ .../tests/farm_with_locked_rewards_test.rs | 233 ++++++++++++++++++ dex/farm/tests/total_farm_position_test.rs | 58 ----- 3 files changed, 320 insertions(+), 58 deletions(-) diff --git a/dex/farm-with-locked-rewards/tests/farm_with_locked_rewards_setup/mod.rs b/dex/farm-with-locked-rewards/tests/farm_with_locked_rewards_setup/mod.rs index 7125f474b..98c8837ee 100644 --- a/dex/farm-with-locked-rewards/tests/farm_with_locked_rewards_setup/mod.rs +++ b/dex/farm-with-locked-rewards/tests/farm_with_locked_rewards_setup/mod.rs @@ -8,6 +8,7 @@ use multiversx_sc::{ types::{Address, BigInt, EsdtLocalRole, MultiValueEncoded}, }; use multiversx_sc_scenario::{ + imports::TxTokenTransfer, managed_address, managed_biguint, managed_token_id, rust_biguint, whitebox_legacy::{BlockchainStateWrapper, ContractObjWrapper}, DebugApi, @@ -24,6 +25,7 @@ use farm_with_locked_rewards::Farm; use locking_module::lock_with_energy_module::LockWithEnergyModule; use multiversx_sc_modules::pause::PauseModule; use pausable::{PausableModule, State}; +use rewards::RewardsModule; use sc_whitelist_module::SCWhitelistModule; use simple_lock::locked_token::LockedTokenModule; use week_timekeeping::Epoch; @@ -48,6 +50,11 @@ pub const EPOCHS_IN_YEAR: u64 = 360; pub static LOCK_OPTIONS: &[u64] = &[EPOCHS_IN_YEAR, 2 * EPOCHS_IN_YEAR, 4 * EPOCHS_IN_YEAR]; pub static PENALTY_PERCENTAGES: &[u64] = &[4_000, 6_000, 8_000]; +pub struct NonceAmountPair { + pub nonce: u64, + pub amount: u64, +} + pub struct RawFarmTokenAttributes { pub reward_per_share_bytes: Vec, pub entering_epoch: Epoch, @@ -408,6 +415,37 @@ where result } + pub fn merge_farm_tokens(&mut self, user: &Address, farm_tokens: Vec) { + self.last_farm_token_nonce += 1; + let mut expected_farm_token_amount = 0; + let mut payments = Vec::new(); + for farm_token in farm_tokens { + expected_farm_token_amount += farm_token.amount; + payments.push(TxTokenTransfer { + token_identifier: FARM_TOKEN_ID.to_vec(), + nonce: farm_token.nonce, + value: rust_biguint!(farm_token.amount), + }); + } + + self.b_mock + .execute_esdt_multi_transfer(user, &self.farm_wrapper, &payments, |sc| { + let (out_farm_token, _boosted_rewards) = sc + .merge_farm_tokens_endpoint(OptionalValue::None) + .into_tuple(); + assert_eq!( + out_farm_token.token_identifier, + managed_token_id!(FARM_TOKEN_ID) + ); + assert_eq!(out_farm_token.token_nonce, self.last_farm_token_nonce); + assert_eq!( + out_farm_token.amount, + managed_biguint!(expected_farm_token_amount) + ); + }) + .assert_ok(); + } + pub fn exit_farm(&mut self, user: &Address, farm_token_nonce: u64, exit_farm_amount: u64) { self.b_mock .execute_esdt_transfer( @@ -422,4 +460,53 @@ where ) .assert_ok(); } + + pub fn claim_boosted_rewards_for_user( + &mut self, + owner: &Address, + broker: &Address, + locked_reward_nonce: u64, + ) -> u64 { + self.last_farm_token_nonce += 1; + + let mut result = 0; + self.b_mock + .execute_tx(broker, &self.farm_wrapper, &rust_biguint!(0), |sc| { + let reward_payment = + sc.claim_boosted_rewards(OptionalValue::Some(managed_address!(owner))); + assert_eq!( + reward_payment.token_identifier, + managed_token_id!(LOCKED_REWARD_TOKEN_ID) + ); + assert_eq!(reward_payment.token_nonce, locked_reward_nonce); + + result = reward_payment.amount.to_u64().unwrap(); + }) + .assert_ok(); + + result + } + + pub fn check_farm_token_supply(&mut self, expected_farm_token_supply: u64) { + let b_mock = &mut self.b_mock; + b_mock + .execute_query(&self.farm_wrapper, |sc| { + let actual_farm_supply = sc.farm_token_supply().get(); + assert_eq!( + managed_biguint!(expected_farm_token_supply), + actual_farm_supply + ); + }) + .assert_ok(); + } + + pub fn check_farm_rps(&mut self, expected_amount: u64) { + let b_mock = &mut self.b_mock; + b_mock + .execute_query(&self.farm_wrapper, |sc| { + let current_rps = sc.reward_per_share().get(); + assert_eq!(managed_biguint!(expected_amount), current_rps); + }) + .assert_ok(); + } } diff --git a/dex/farm-with-locked-rewards/tests/farm_with_locked_rewards_test.rs b/dex/farm-with-locked-rewards/tests/farm_with_locked_rewards_test.rs index 8a9fb8793..4668665a4 100644 --- a/dex/farm-with-locked-rewards/tests/farm_with_locked_rewards_test.rs +++ b/dex/farm-with-locked-rewards/tests/farm_with_locked_rewards_test.rs @@ -1,6 +1,8 @@ #![allow(deprecated)] use common_structs::FarmTokenAttributes; +use farm_with_locked_rewards_setup::NonceAmountPair; +use multiversx_sc::codec::Empty; use multiversx_sc_scenario::{managed_address, managed_biguint, rust_biguint, DebugApi}; use simple_lock::locked_token::LockedTokenAttributes; @@ -335,3 +337,234 @@ fn total_farm_position_claim_with_locked_rewards_test() { None, ); } + +#[test] +fn claim_only_boosted_rewards_per_week_test() { + DebugApi::dummy(); + let mut farm_setup = FarmSetup::new( + farm_with_locked_rewards::contract_obj, + energy_factory::contract_obj, + ); + + farm_setup.set_boosted_yields_rewards_percentage(BOOSTED_YIELDS_PERCENTAGE); + farm_setup.set_boosted_yields_factors(); + farm_setup.b_mock.set_block_epoch(2); + + let temp_user = farm_setup.third_user.clone(); + + // first user enter farm + let farm_in_amount = 100_000_000; + let first_user = farm_setup.first_user.clone(); + farm_setup.set_user_energy(&first_user, 1_000, 2, 1); + farm_setup.enter_farm(&first_user, farm_in_amount); + + farm_setup.check_farm_token_supply(farm_in_amount); + farm_setup.check_farm_rps(0u64); + + farm_setup.b_mock.set_block_nonce(10); + farm_setup.b_mock.set_block_epoch(6); + farm_setup.set_user_energy(&first_user, 1_000, 6, 1); + farm_setup.set_user_energy(&temp_user, 1, 6, 1); + farm_setup.enter_farm(&temp_user, 1); + farm_setup.exit_farm(&temp_user, 2, 1); + + farm_setup.check_farm_rps(75_000_000u64); + + // advance 1 week + farm_setup.set_user_energy(&first_user, 1_000, 13, 1); + farm_setup.b_mock.set_block_nonce(20); + farm_setup.b_mock.set_block_epoch(13); + + let boosted_rewards = 2_500; + let second_week_received_reward_amt = + farm_setup.claim_boosted_rewards_for_user(&first_user, &first_user, 1); + + assert_eq!(second_week_received_reward_amt, boosted_rewards); + farm_setup.check_farm_rps(150_000_000u64); + + // advance 1 week + farm_setup.set_user_energy(&first_user, 1_000, 15, 1); + farm_setup.b_mock.set_block_nonce(30); + farm_setup.b_mock.set_block_epoch(15); + let third_week_received_reward_amt = + farm_setup.claim_boosted_rewards_for_user(&first_user, &first_user, 1); + + assert_eq!(third_week_received_reward_amt, boosted_rewards); + farm_setup.check_farm_rps(225_000_000u64); + + farm_setup.b_mock.check_nft_balance::( + &first_user, + LOCKED_REWARD_TOKEN_ID, + 1, + &rust_biguint!(boosted_rewards * 2), + None, + ); +} + +#[test] +fn claim_rewards_per_week_test() { + DebugApi::dummy(); + let mut farm_setup = FarmSetup::new( + farm_with_locked_rewards::contract_obj, + energy_factory::contract_obj, + ); + + farm_setup.set_boosted_yields_rewards_percentage(BOOSTED_YIELDS_PERCENTAGE); + farm_setup.set_boosted_yields_factors(); + farm_setup.b_mock.set_block_epoch(2); + + let temp_user = farm_setup.third_user.clone(); + + // first user enter farm + let farm_in_amount = 100_000_000; + let first_user = farm_setup.first_user.clone(); + farm_setup.set_user_energy(&first_user, 1_000, 2, 1); + farm_setup.enter_farm(&first_user, farm_in_amount); + + farm_setup.check_farm_token_supply(farm_in_amount); + farm_setup.check_farm_rps(0u64); + + farm_setup.b_mock.set_block_nonce(10); + farm_setup.b_mock.set_block_epoch(6); + farm_setup.set_user_energy(&first_user, 1_000, 6, 1); + farm_setup.set_user_energy(&temp_user, 1, 6, 1); + farm_setup.enter_farm(&temp_user, 1); + farm_setup.exit_farm(&temp_user, 2, 1); + + farm_setup.check_farm_rps(75_000_000u64); + let base_rewards_per_week = 7_500; + let boosted_rewards_per_week = 2_500; + let total_rewards_per_week = base_rewards_per_week + boosted_rewards_per_week; + + // advance 1 week + farm_setup.set_user_energy(&first_user, 1_000, 13, 1); + farm_setup.b_mock.set_block_nonce(20); + farm_setup.b_mock.set_block_epoch(13); + + let second_week_received_reward_amt = farm_setup.claim_rewards(&first_user, 1, farm_in_amount); + + assert_eq!( + second_week_received_reward_amt, + total_rewards_per_week + base_rewards_per_week + ); + farm_setup.check_farm_rps(150_000_000u64); + + // advance 1 week + farm_setup.set_user_energy(&first_user, 1_000, 15, 1); + farm_setup.b_mock.set_block_nonce(30); + farm_setup.b_mock.set_block_epoch(15); + let third_week_received_reward_amt = farm_setup.claim_rewards(&first_user, 3, farm_in_amount); + + assert_eq!(third_week_received_reward_amt, total_rewards_per_week); + farm_setup.check_farm_rps(225_000_000u64); + + farm_setup.b_mock.check_nft_balance::( + &first_user, + LOCKED_REWARD_TOKEN_ID, + 1, + &rust_biguint!(total_rewards_per_week * 2 + base_rewards_per_week), + None, + ); +} + +#[test] +fn merge_farm_position_per_week_test() { + DebugApi::dummy(); + let mut farm_setup = FarmSetup::new( + farm_with_locked_rewards::contract_obj, + energy_factory::contract_obj, + ); + + farm_setup.set_boosted_yields_rewards_percentage(BOOSTED_YIELDS_PERCENTAGE); + farm_setup.set_boosted_yields_factors(); + farm_setup.b_mock.set_block_epoch(2); + + let temp_user = farm_setup.third_user.clone(); + + // first user enter farm + let farm_in_amount = 10_000_000; + let first_user = farm_setup.first_user.clone(); + farm_setup.set_user_energy(&first_user, 1_000, 2, 1); + + // User creates 3 different positions + farm_setup.enter_farm(&first_user, farm_in_amount); + farm_setup.enter_farm(&first_user, farm_in_amount); + farm_setup.enter_farm(&first_user, farm_in_amount); + + farm_setup.check_farm_token_supply(farm_in_amount * 3); + farm_setup.check_farm_rps(0u64); + + farm_setup.b_mock.set_block_nonce(10); + farm_setup.b_mock.set_block_epoch(6); + farm_setup.set_user_energy(&first_user, 1_000, 6, 1); + farm_setup.set_user_energy(&temp_user, 1, 6, 1); + farm_setup.enter_farm(&temp_user, 1); + farm_setup.exit_farm(&temp_user, 4, 1); + + farm_setup.check_farm_rps(250_000_000u64); + let boosted_rewards_per_week = 2_500; + + // advance 1 week + farm_setup.set_user_energy(&first_user, 1_000, 13, 1); + farm_setup.b_mock.set_block_nonce(20); + farm_setup.b_mock.set_block_epoch(13); + + let mut payments = vec![ + NonceAmountPair { + nonce: 1, + amount: farm_in_amount, + }, + NonceAmountPair { + nonce: 2, + amount: farm_in_amount, + }, + ]; + + farm_setup.merge_farm_tokens(&first_user, payments); + + farm_setup.check_farm_token_supply(farm_in_amount * 3); + farm_setup.check_farm_rps(500_000_000u64); + farm_setup.b_mock.check_nft_balance::( + &first_user, + LOCKED_REWARD_TOKEN_ID, + 1, + &rust_biguint!(boosted_rewards_per_week), + None, + ); + + // advance 1 week + farm_setup.set_user_energy(&first_user, 1_000, 15, 1); + farm_setup.b_mock.set_block_nonce(30); + farm_setup.b_mock.set_block_epoch(15); + + payments = vec![ + NonceAmountPair { + nonce: 3, + amount: farm_in_amount, + }, + NonceAmountPair { + nonce: 5, + amount: farm_in_amount * 2, + }, + ]; + + farm_setup.merge_farm_tokens(&first_user, payments); + + farm_setup.check_farm_token_supply(farm_in_amount * 3); + farm_setup.check_farm_rps(750_000_000u64); + + farm_setup.b_mock.check_nft_balance::( + &first_user, + FARM_TOKEN_ID, + 6, + &rust_biguint!(farm_in_amount * 3), + None, + ); + farm_setup.b_mock.check_nft_balance::( + &first_user, + LOCKED_REWARD_TOKEN_ID, + 1, + &rust_biguint!(boosted_rewards_per_week * 2), + None, + ); +} diff --git a/dex/farm/tests/total_farm_position_test.rs b/dex/farm/tests/total_farm_position_test.rs index e0819d411..b7ca1be7b 100644 --- a/dex/farm/tests/total_farm_position_test.rs +++ b/dex/farm/tests/total_farm_position_test.rs @@ -1193,61 +1193,3 @@ fn total_farm_position_through_simple_lock_test() { &rust_biguint!(first_received_reward_amt), ); } - -#[test] -fn claim_only_boosted_rewards_per_week_test() { - DebugApi::dummy(); - let mut farm_setup = MultiUserFarmSetup::new( - farm::contract_obj, - energy_factory_mock::contract_obj, - energy_update::contract_obj, - ); - - farm_setup.set_boosted_yields_rewards_percentage(BOOSTED_YIELDS_PERCENTAGE); - farm_setup.set_boosted_yields_factors(); - farm_setup.b_mock.set_block_epoch(2); - - let temp_user = farm_setup.third_user.clone(); - - // first user enter farm - let farm_in_amount = 100_000_000; - let first_user = farm_setup.first_user.clone(); - farm_setup.set_user_energy(&first_user, 1_000, 2, 1); - farm_setup.enter_farm(&first_user, farm_in_amount); - - farm_setup.check_farm_token_supply(farm_in_amount); - - farm_setup.b_mock.set_block_nonce(10); - farm_setup.b_mock.set_block_epoch(6); - farm_setup.set_user_energy(&first_user, 1_000, 6, 1); - farm_setup.set_user_energy(&temp_user, 1, 6, 1); - farm_setup.enter_farm(&temp_user, 1); - farm_setup.exit_farm(&temp_user, 2, 1); - - // advance 1 week - farm_setup.set_user_energy(&first_user, 1_000, 13, 1); - farm_setup.b_mock.set_block_nonce(20); - farm_setup.b_mock.set_block_epoch(13); - - let boosted_rewards = 2_500; - let second_week_received_reward_amt = - farm_setup.claim_boosted_rewards_for_user(&first_user, &first_user); - - assert_eq!(second_week_received_reward_amt, boosted_rewards); - - // advance 1 week - farm_setup.set_user_energy(&first_user, 1_000, 15, 1); - farm_setup.b_mock.set_block_nonce(30); - farm_setup.b_mock.set_block_epoch(15); - let third_week_received_reward_amt = - farm_setup.claim_boosted_rewards_for_user(&first_user, &first_user); - - // Should be equal to half base generated rewards + full boosted generated rewards - assert_eq!(third_week_received_reward_amt, boosted_rewards); - - farm_setup.b_mock.check_esdt_balance( - &first_user, - REWARD_TOKEN_ID, - &rust_biguint!(boosted_rewards * 2), - ); -} From 602b696735ab9ce1bda52049113e2bced3c94efe Mon Sep 17 00:00:00 2001 From: Sorin Petreasca Date: Fri, 2 Aug 2024 16:54:34 +0300 Subject: [PATCH 2/4] farm staking unit tests --- .../tests/farm_staking_energy_test.rs | 303 ++++++++++++++++++ .../tests/farm_staking_setup/mod.rs | 67 ++++ 2 files changed, 370 insertions(+) diff --git a/farm-staking/farm-staking/tests/farm_staking_energy_test.rs b/farm-staking/farm-staking/tests/farm_staking_energy_test.rs index 1274be166..e71f0db34 100644 --- a/farm-staking/farm-staking/tests/farm_staking_energy_test.rs +++ b/farm-staking/farm-staking/tests/farm_staking_energy_test.rs @@ -1427,3 +1427,306 @@ fn boosted_rewards_config_change_test() { && first_user_total_rewards == third_user_total_rewards ); } + +#[test] +fn claim_only_boosted_rewards_per_week_test() { + DebugApi::dummy(); + let mut fs_setup = + FarmStakingSetup::new(farm_staking::contract_obj, energy_factory::contract_obj); + + fs_setup.set_boosted_yields_factors(); + fs_setup.set_boosted_yields_rewards_percentage(BOOSTED_YIELDS_PERCENTAGE); + + let first_user = fs_setup.user_address.clone(); + let farm_in_amount = 100_000_000; + + fs_setup.set_user_energy(&first_user, 10_000, 0, 10); + fs_setup.stake_farm(&first_user, farm_in_amount, &[], 1, 0, 0); + + fs_setup.check_farm_token_supply(farm_in_amount); + fs_setup.check_farm_rps(0u64); + + fs_setup.b_mock.set_block_nonce(100); + fs_setup.b_mock.set_block_epoch(6); + fs_setup.set_user_energy(&first_user, 1_000, 6, 1); + + // Reset user balance + fs_setup + .b_mock + .set_esdt_balance(&first_user, FARMING_TOKEN_ID, &rust_biguint!(0)); + + // random user tx to collect rewards + let rand_user = fs_setup.b_mock.create_user_account(&rust_biguint!(0)); + fs_setup.b_mock.set_esdt_balance( + &rand_user, + FARMING_TOKEN_ID, + &rust_biguint!(USER_TOTAL_RIDE_TOKENS), + ); + + fs_setup.set_user_energy(&rand_user, 1, 6, 1); + fs_setup.stake_farm(&rand_user, 10, &[], 2, 3_000_000u64, 0); + fs_setup.unstake_farm_no_checks(&rand_user, 10, 2); + + let farm_rps_increase = 3_000_000u64; + let mut current_farm_rps = 0; + current_farm_rps += farm_rps_increase; + fs_setup.check_farm_rps(current_farm_rps); + + // advance 1 week + fs_setup.set_user_energy(&first_user, 1_000, 13, 1); + fs_setup.b_mock.set_block_nonce(200); + fs_setup.b_mock.set_block_epoch(13); + + let boosted_rewards_for_week = 100; + fs_setup.claim_boosted_rewards_for_user( + &first_user, + &first_user, + boosted_rewards_for_week, + &rust_biguint!(boosted_rewards_for_week), + ); + + current_farm_rps += farm_rps_increase; + fs_setup.check_farm_rps(current_farm_rps); + + // advance 1 week + fs_setup.set_user_energy(&first_user, 1_000, 15, 1); + fs_setup.b_mock.set_block_nonce(300); + fs_setup.b_mock.set_block_epoch(15); + fs_setup.claim_boosted_rewards_for_user( + &first_user, + &first_user, + boosted_rewards_for_week, + &rust_biguint!(boosted_rewards_for_week * 2), + ); + + current_farm_rps += farm_rps_increase; + fs_setup.check_farm_rps(current_farm_rps); + fs_setup.b_mock.check_esdt_balance( + &first_user, + REWARD_TOKEN_ID, + &rust_biguint!(boosted_rewards_for_week * 2), + ); + + let expected_attributes = StakingFarmTokenAttributes:: { + reward_per_share: managed_biguint!(0), + compounded_reward: managed_biguint!(0), + current_farm_amount: managed_biguint!(farm_in_amount), + original_owner: managed_address!(&first_user), + }; + + fs_setup.b_mock.check_nft_balance( + &first_user, + FARM_TOKEN_ID, + 1, + &rust_biguint!(farm_in_amount), + Some(&expected_attributes), + ); +} + +#[test] +fn claim_rewards_per_week_test() { + DebugApi::dummy(); + let mut fs_setup = + FarmStakingSetup::new(farm_staking::contract_obj, energy_factory::contract_obj); + + fs_setup.set_boosted_yields_factors(); + fs_setup.set_boosted_yields_rewards_percentage(BOOSTED_YIELDS_PERCENTAGE); + + let first_user = fs_setup.user_address.clone(); + let farm_in_amount = 100_000_000; + + fs_setup.set_user_energy(&first_user, 10_000, 0, 10); + fs_setup.stake_farm(&first_user, farm_in_amount, &[], 1, 0, 0); + + fs_setup.check_farm_token_supply(farm_in_amount); + fs_setup.check_farm_rps(0u64); + + fs_setup.b_mock.set_block_nonce(100); + fs_setup.b_mock.set_block_epoch(6); + fs_setup.set_user_energy(&first_user, 1_000, 6, 1); + + // Reset user balance + fs_setup + .b_mock + .set_esdt_balance(&first_user, FARMING_TOKEN_ID, &rust_biguint!(0)); + + // random user tx to collect rewards + let rand_user = fs_setup.b_mock.create_user_account(&rust_biguint!(0)); + fs_setup.b_mock.set_esdt_balance( + &rand_user, + FARMING_TOKEN_ID, + &rust_biguint!(USER_TOTAL_RIDE_TOKENS), + ); + + fs_setup.set_user_energy(&rand_user, 1, 6, 1); + fs_setup.stake_farm(&rand_user, 10, &[], 2, 3_000_000u64, 0); + fs_setup.unstake_farm_no_checks(&rand_user, 10, 2); + + let farm_rps_increase = 3_000_000u64; + let mut current_farm_rps = 0; + current_farm_rps += farm_rps_increase; + fs_setup.check_farm_rps(current_farm_rps); + + // advance 1 week + fs_setup.set_user_energy(&first_user, 1_000, 13, 1); + fs_setup.b_mock.set_block_nonce(200); + fs_setup.b_mock.set_block_epoch(13); + + let base_rewards_for_week = 300; + let boosted_rewards_for_week = 100; + + current_farm_rps += farm_rps_increase; + let mut user_rewards_balance = base_rewards_for_week * 2 + boosted_rewards_for_week; + fs_setup.claim_rewards( + &first_user, + farm_in_amount, + 1, + base_rewards_for_week * 2 + boosted_rewards_for_week, + &rust_biguint!(user_rewards_balance), + &rust_biguint!(user_rewards_balance), // user balance has bet set to 0 at the start + 4, + current_farm_rps, + ); + + fs_setup.check_farm_rps(current_farm_rps); + + // advance 1 week + fs_setup.set_user_energy(&first_user, 1_000, 15, 1); + fs_setup.b_mock.set_block_nonce(300); + fs_setup.b_mock.set_block_epoch(15); + + current_farm_rps += farm_rps_increase; + user_rewards_balance += base_rewards_for_week + boosted_rewards_for_week; + fs_setup.claim_rewards( + &first_user, + farm_in_amount, + 4, + base_rewards_for_week + boosted_rewards_for_week, + &rust_biguint!(user_rewards_balance), + &rust_biguint!(user_rewards_balance), + 5, + current_farm_rps, + ); + + fs_setup.check_farm_rps(current_farm_rps); + + fs_setup.b_mock.check_esdt_balance( + &first_user, + REWARD_TOKEN_ID, + &rust_biguint!(user_rewards_balance), + ); +} + +#[test] +fn merge_farm_position_per_week_test() { + DebugApi::dummy(); + let mut fs_setup = + FarmStakingSetup::new(farm_staking::contract_obj, energy_factory::contract_obj); + + fs_setup.set_boosted_yields_factors(); + fs_setup.set_boosted_yields_rewards_percentage(BOOSTED_YIELDS_PERCENTAGE); + + let first_user = fs_setup.user_address.clone(); + let farm_in_amount = 100_000_000; + + fs_setup.set_user_energy(&first_user, 10_000, 0, 10); + fs_setup.stake_farm(&first_user, farm_in_amount, &[], 1, 0, 0); + fs_setup.stake_farm(&first_user, farm_in_amount, &[], 2, 0, 0); + fs_setup.stake_farm(&first_user, farm_in_amount, &[], 3, 0, 0); + + fs_setup.check_farm_token_supply(farm_in_amount * 3); + fs_setup.check_farm_rps(0u64); + + fs_setup.b_mock.set_block_nonce(100); + fs_setup.b_mock.set_block_epoch(6); + fs_setup.set_user_energy(&first_user, 1_000, 6, 1); + + // Reset user balance + fs_setup + .b_mock + .set_esdt_balance(&first_user, FARMING_TOKEN_ID, &rust_biguint!(0)); + + // random user tx to collect rewards + let rand_user = fs_setup.b_mock.create_user_account(&rust_biguint!(0)); + fs_setup.b_mock.set_esdt_balance( + &rand_user, + FARMING_TOKEN_ID, + &rust_biguint!(USER_TOTAL_RIDE_TOKENS), + ); + + fs_setup.set_user_energy(&rand_user, 1, 6, 1); + fs_setup.stake_farm(&rand_user, 10, &[], 4, 3_500_000u64, 0); + fs_setup.unstake_farm_no_checks(&rand_user, 10, 4); + + let farm_rps_increase = 3_500_000u64; + let mut current_farm_rps = 0; + current_farm_rps += farm_rps_increase; + fs_setup.check_farm_rps(current_farm_rps); + + // advance 1 week + fs_setup.set_user_energy(&first_user, 1_000, 13, 1); + fs_setup.b_mock.set_block_nonce(200); + fs_setup.b_mock.set_block_epoch(13); + + let boosted_rewards_for_week = 350u64; + + current_farm_rps += farm_rps_increase; + let mut user_rewards_balance = boosted_rewards_for_week; + + let mut payments = vec![ + NonceAmountPair { + nonce: 1, + amount: farm_in_amount, + }, + NonceAmountPair { + nonce: 2, + amount: farm_in_amount, + }, + ]; + fs_setup.merge_farm_tokens(&first_user, payments, 6); + + fs_setup.check_farm_rps(current_farm_rps); + + // advance 1 week + fs_setup.set_user_energy(&first_user, 1_000, 15, 1); + fs_setup.b_mock.set_block_nonce(300); + fs_setup.b_mock.set_block_epoch(15); + + current_farm_rps += farm_rps_increase; + user_rewards_balance += boosted_rewards_for_week; + + payments = vec![ + NonceAmountPair { + nonce: 3, + amount: farm_in_amount, + }, + NonceAmountPair { + nonce: 6, + amount: farm_in_amount * 2, + }, + ]; + fs_setup.merge_farm_tokens(&first_user, payments, 7); + + fs_setup.check_farm_rps(current_farm_rps); + + fs_setup.b_mock.check_esdt_balance( + &first_user, + REWARD_TOKEN_ID, + &rust_biguint!(user_rewards_balance), + ); + + let expected_attributes = StakingFarmTokenAttributes:: { + reward_per_share: managed_biguint!(0), + compounded_reward: managed_biguint!(0), + current_farm_amount: managed_biguint!(farm_in_amount * 3), + original_owner: managed_address!(&first_user), + }; + + fs_setup.b_mock.check_nft_balance( + &first_user, + FARM_TOKEN_ID, + 7, + &rust_biguint!(farm_in_amount * 3), + Some(&expected_attributes), + ); +} diff --git a/farm-staking/farm-staking/tests/farm_staking_setup/mod.rs b/farm-staking/farm-staking/tests/farm_staking_setup/mod.rs index 90513e2d6..45151e926 100644 --- a/farm-staking/farm-staking/tests/farm_staking_setup/mod.rs +++ b/farm-staking/farm-staking/tests/farm_staking_setup/mod.rs @@ -25,6 +25,7 @@ use farm_staking::unstake_farm::UnstakeFarmModule; use farm_staking::*; use farm_token::FarmTokenModule; use pausable::{PausableModule, State}; +use rewards::RewardsModule; pub static REWARD_TOKEN_ID: &[u8] = b"RIDE-abcdef"; // reward token ID pub static FARMING_TOKEN_ID: &[u8] = b"RIDE-abcdef"; // farming token ID @@ -46,6 +47,11 @@ pub const MIN_FARM_AMOUNT_FOR_BOOSTED_YIELDS: u64 = 1; pub const WITHDRAW_AMOUNT_TOO_HIGH: &str = "Withdraw amount is higher than the remaining uncollected rewards!"; +pub struct NonceAmountPair { + pub nonce: u64, + pub amount: u64, +} + pub struct FarmStakingSetup where FarmObjBuilder: 'static + Copy + Fn() -> farm_staking::ContractObj, @@ -509,6 +515,24 @@ where ); } + pub fn unstake_farm_no_checks( + &mut self, + user: &Address, + farm_token_amount: u64, + farm_token_nonce: u64, + ) { + let _ = self.b_mock.execute_esdt_transfer( + user, + &self.farm_wrapper, + FARM_TOKEN_ID, + farm_token_nonce, + &rust_biguint!(farm_token_amount), + |sc| { + sc.unstake_farm(OptionalValue::None); + }, + ); + } + pub fn unbond_farm( &mut self, farm_token_nonce: u64, @@ -542,6 +566,40 @@ where ); } + pub fn merge_farm_tokens( + &mut self, + user: &Address, + farm_tokens: Vec, + expected_farm_token_nonce: u64, + ) { + let mut expected_farm_token_amount = 0; + let mut payments = Vec::new(); + for farm_token in farm_tokens { + expected_farm_token_amount += farm_token.amount; + payments.push(TxTokenTransfer { + token_identifier: FARM_TOKEN_ID.to_vec(), + nonce: farm_token.nonce, + value: rust_biguint!(farm_token.amount), + }); + } + + self.b_mock + .execute_esdt_multi_transfer(user, &self.farm_wrapper, &payments, |sc| { + let (out_farm_token, _boosted_rewards) = + sc.merge_farm_tokens_endpoint().into_tuple(); + assert_eq!( + out_farm_token.token_identifier, + managed_token_id!(FARM_TOKEN_ID) + ); + assert_eq!(out_farm_token.token_nonce, expected_farm_token_nonce); + assert_eq!( + out_farm_token.amount, + managed_biguint!(expected_farm_token_amount) + ); + }) + .assert_ok(); + } + pub fn check_farm_token_supply(&mut self, expected_farm_token_supply: u64) { self.b_mock .execute_query(&self.farm_wrapper, |sc| { @@ -554,6 +612,15 @@ where .assert_ok(); } + pub fn check_farm_rps(&mut self, expected_amount: u64) { + self.b_mock + .execute_query(&self.farm_wrapper, |sc| { + let current_rps = sc.reward_per_share().get(); + assert_eq!(managed_biguint!(expected_amount), current_rps); + }) + .assert_ok(); + } + pub fn check_rewards_capacity(&mut self, expected_farm_token_supply: u64) { self.b_mock .execute_query(&self.farm_wrapper, |sc| { From b147571ad81bca791d612fd6247af4c54edb1640 Mon Sep 17 00:00:00 2001 From: Sorin Petreasca Date: Fri, 2 Aug 2024 18:01:55 +0300 Subject: [PATCH 3/4] farm staking unbond fix + unit test --- farm-staking/farm-staking/src/unbond_farm.rs | 2 + .../tests/farm_staking_energy_test.rs | 92 +++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/farm-staking/farm-staking/src/unbond_farm.rs b/farm-staking/farm-staking/src/unbond_farm.rs index ec2044897..de296e4c3 100644 --- a/farm-staking/farm-staking/src/unbond_farm.rs +++ b/farm-staking/farm-staking/src/unbond_farm.rs @@ -49,6 +49,8 @@ pub trait UnbondFarmModule: "Unbond period not over" ); + let current_week = self.get_current_week(); + self.perform_weekly_update(current_week); FarmStakingWrapper::::generate_aggregated_rewards(self, &mut storage_cache); self.set_farm_supply_for_current_week(&storage_cache.farm_token_supply); diff --git a/farm-staking/farm-staking/tests/farm_staking_energy_test.rs b/farm-staking/farm-staking/tests/farm_staking_energy_test.rs index e71f0db34..dee471634 100644 --- a/farm-staking/farm-staking/tests/farm_staking_energy_test.rs +++ b/farm-staking/farm-staking/tests/farm_staking_energy_test.rs @@ -1730,3 +1730,95 @@ fn merge_farm_position_per_week_test() { Some(&expected_attributes), ); } + +#[test] +fn unbond_farm_position_per_week_test() { + DebugApi::dummy(); + let mut fs_setup = + FarmStakingSetup::new(farm_staking::contract_obj, energy_factory::contract_obj); + + fs_setup.set_boosted_yields_factors(); + fs_setup.set_boosted_yields_rewards_percentage(BOOSTED_YIELDS_PERCENTAGE); + + let first_user = fs_setup.user_address.clone(); + let farm_in_amount = 100_000_000; + + fs_setup.set_user_energy(&first_user, 10_000, 0, 10); + fs_setup.stake_farm(&first_user, farm_in_amount, &[], 1, 0, 0); + fs_setup.check_farm_token_supply(farm_in_amount); + + fs_setup.unstake_farm_no_checks(&first_user, farm_in_amount / 2, 1); + fs_setup.check_farm_token_supply(farm_in_amount / 2); + + fs_setup.check_farm_rps(0u64); + + fs_setup.b_mock.set_block_nonce(100); + fs_setup.b_mock.set_block_epoch(6); + fs_setup.set_user_energy(&first_user, 1_000, 6, 1); + + // Reset user balance + fs_setup + .b_mock + .set_esdt_balance(&first_user, FARMING_TOKEN_ID, &rust_biguint!(0)); + + let mut current_farm_rps = 0; + fs_setup.check_farm_rps(current_farm_rps); + + // advance 1 week + fs_setup.set_user_energy(&first_user, 1_000, 13, 1); + fs_setup.b_mock.set_block_nonce(200); + fs_setup.b_mock.set_block_epoch(13); + + fs_setup.unbond_farm( + 2, + farm_in_amount / 2, + farm_in_amount / 2, + farm_in_amount / 2, + ); + + let mut user_balance = farm_in_amount / 2; + fs_setup + .b_mock + .check_esdt_balance(&first_user, REWARD_TOKEN_ID, &rust_biguint!(user_balance)); + + let farm_rps_increase = 6_000_000u64; + current_farm_rps += farm_rps_increase; + fs_setup.check_farm_rps(current_farm_rps); + + // advance 1 week + fs_setup.set_user_energy(&first_user, 1_000, 15, 1); + fs_setup.b_mock.set_block_nonce(300); + fs_setup.b_mock.set_block_epoch(15); + + let boosted_rewards_for_week = 100; + user_balance += boosted_rewards_for_week; + fs_setup.claim_boosted_rewards_for_user( + &first_user, + &first_user, + boosted_rewards_for_week, + &rust_biguint!(user_balance), + ); + + // User should have the following balance: unstake_amount + boosted_rewards_week_2 + fs_setup + .b_mock + .check_esdt_balance(&first_user, REWARD_TOKEN_ID, &rust_biguint!(user_balance)); + + current_farm_rps += farm_rps_increase / 2; //user existed with half position + fs_setup.check_farm_rps(current_farm_rps); + + let expected_attributes = StakingFarmTokenAttributes:: { + reward_per_share: managed_biguint!(0), + compounded_reward: managed_biguint!(0), + current_farm_amount: managed_biguint!(farm_in_amount), + original_owner: managed_address!(&first_user), + }; + + fs_setup.b_mock.check_nft_balance( + &first_user, + FARM_TOKEN_ID, + 1, + &rust_biguint!(farm_in_amount / 2), + Some(&expected_attributes), + ); +} From a28f0a4381bdaf82ae58c3c759a2ff09e39c7bdb Mon Sep 17 00:00:00 2001 From: Sorin Petreasca Date: Fri, 2 Aug 2024 18:12:39 +0300 Subject: [PATCH 4/4] revert farm staking unbond changes --- farm-staking/farm-staking/src/unbond_farm.rs | 11 +-- .../tests/farm_staking_energy_test.rs | 92 ------------------- 2 files changed, 2 insertions(+), 101 deletions(-) diff --git a/farm-staking/farm-staking/src/unbond_farm.rs b/farm-staking/farm-staking/src/unbond_farm.rs index de296e4c3..0e661616e 100644 --- a/farm-staking/farm-staking/src/unbond_farm.rs +++ b/farm-staking/farm-staking/src/unbond_farm.rs @@ -1,9 +1,8 @@ multiversx_sc::imports!(); use contexts::storage_cache::StorageCache; -use farm_base_impl::base_traits_impl::FarmContract; -use crate::{base_impl_wrapper::FarmStakingWrapper, token_attributes::UnbondSftAttributes}; +use crate::token_attributes::UnbondSftAttributes; #[multiversx_sc::module] pub trait UnbondFarmModule: @@ -17,7 +16,6 @@ pub trait UnbondFarmModule: + pausable::PausableModule + permissions_module::PermissionsModule + multiversx_sc_modules::default_issue_callbacks::DefaultIssueCallbacksModule - + farm_base_impl::base_farm_init::BaseFarmInitModule + farm_base_impl::base_farm_validation::BaseFarmValidationModule + utils::UtilsModule + farm_boosted_yields::FarmBoostedYieldsModule @@ -33,7 +31,7 @@ pub trait UnbondFarmModule: #[payable("*")] #[endpoint(unbondFarm)] fn unbond_farm(&self) -> EsdtTokenPayment { - let mut storage_cache = StorageCache::new(self); + let storage_cache = StorageCache::new(self); self.validate_contract_state(storage_cache.contract_state, &storage_cache.farm_token_id); let farm_token_mapper = self.farm_token(); @@ -49,11 +47,6 @@ pub trait UnbondFarmModule: "Unbond period not over" ); - let current_week = self.get_current_week(); - self.perform_weekly_update(current_week); - FarmStakingWrapper::::generate_aggregated_rewards(self, &mut storage_cache); - self.set_farm_supply_for_current_week(&storage_cache.farm_token_supply); - farm_token_mapper.nft_burn(payment.token_nonce, &payment.amount); let caller = self.blockchain().get_caller(); diff --git a/farm-staking/farm-staking/tests/farm_staking_energy_test.rs b/farm-staking/farm-staking/tests/farm_staking_energy_test.rs index dee471634..e71f0db34 100644 --- a/farm-staking/farm-staking/tests/farm_staking_energy_test.rs +++ b/farm-staking/farm-staking/tests/farm_staking_energy_test.rs @@ -1730,95 +1730,3 @@ fn merge_farm_position_per_week_test() { Some(&expected_attributes), ); } - -#[test] -fn unbond_farm_position_per_week_test() { - DebugApi::dummy(); - let mut fs_setup = - FarmStakingSetup::new(farm_staking::contract_obj, energy_factory::contract_obj); - - fs_setup.set_boosted_yields_factors(); - fs_setup.set_boosted_yields_rewards_percentage(BOOSTED_YIELDS_PERCENTAGE); - - let first_user = fs_setup.user_address.clone(); - let farm_in_amount = 100_000_000; - - fs_setup.set_user_energy(&first_user, 10_000, 0, 10); - fs_setup.stake_farm(&first_user, farm_in_amount, &[], 1, 0, 0); - fs_setup.check_farm_token_supply(farm_in_amount); - - fs_setup.unstake_farm_no_checks(&first_user, farm_in_amount / 2, 1); - fs_setup.check_farm_token_supply(farm_in_amount / 2); - - fs_setup.check_farm_rps(0u64); - - fs_setup.b_mock.set_block_nonce(100); - fs_setup.b_mock.set_block_epoch(6); - fs_setup.set_user_energy(&first_user, 1_000, 6, 1); - - // Reset user balance - fs_setup - .b_mock - .set_esdt_balance(&first_user, FARMING_TOKEN_ID, &rust_biguint!(0)); - - let mut current_farm_rps = 0; - fs_setup.check_farm_rps(current_farm_rps); - - // advance 1 week - fs_setup.set_user_energy(&first_user, 1_000, 13, 1); - fs_setup.b_mock.set_block_nonce(200); - fs_setup.b_mock.set_block_epoch(13); - - fs_setup.unbond_farm( - 2, - farm_in_amount / 2, - farm_in_amount / 2, - farm_in_amount / 2, - ); - - let mut user_balance = farm_in_amount / 2; - fs_setup - .b_mock - .check_esdt_balance(&first_user, REWARD_TOKEN_ID, &rust_biguint!(user_balance)); - - let farm_rps_increase = 6_000_000u64; - current_farm_rps += farm_rps_increase; - fs_setup.check_farm_rps(current_farm_rps); - - // advance 1 week - fs_setup.set_user_energy(&first_user, 1_000, 15, 1); - fs_setup.b_mock.set_block_nonce(300); - fs_setup.b_mock.set_block_epoch(15); - - let boosted_rewards_for_week = 100; - user_balance += boosted_rewards_for_week; - fs_setup.claim_boosted_rewards_for_user( - &first_user, - &first_user, - boosted_rewards_for_week, - &rust_biguint!(user_balance), - ); - - // User should have the following balance: unstake_amount + boosted_rewards_week_2 - fs_setup - .b_mock - .check_esdt_balance(&first_user, REWARD_TOKEN_ID, &rust_biguint!(user_balance)); - - current_farm_rps += farm_rps_increase / 2; //user existed with half position - fs_setup.check_farm_rps(current_farm_rps); - - let expected_attributes = StakingFarmTokenAttributes:: { - reward_per_share: managed_biguint!(0), - compounded_reward: managed_biguint!(0), - current_farm_amount: managed_biguint!(farm_in_amount), - original_owner: managed_address!(&first_user), - }; - - fs_setup.b_mock.check_nft_balance( - &first_user, - FARM_TOKEN_ID, - 1, - &rust_biguint!(farm_in_amount / 2), - Some(&expected_attributes), - ); -}