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 a4046f03e..e1a885355 100644 --- a/farm-staking/farm-staking/tests/farm_staking_energy_test.rs +++ b/farm-staking/farm-staking/tests/farm_staking_energy_test.rs @@ -18,8 +18,11 @@ use multiversx_sc_scenario::{ #[test] fn farm_staking_with_energy_setup_test() { - let mut fs_setup = - FarmStakingSetup::new(farm_staking::contract_obj, energy_factory::contract_obj); + let mut fs_setup = FarmStakingSetup::new( + farm_staking::contract_obj, + energy_factory::contract_obj, + permissions_hub::contract_obj, + ); fs_setup.set_boosted_yields_factors(); fs_setup.set_boosted_yields_rewards_percentage(BOOSTED_YIELDS_PERCENTAGE); @@ -28,8 +31,11 @@ fn farm_staking_with_energy_setup_test() { #[test] fn farm_staking_boosted_rewards_no_energy_test() { DebugApi::dummy(); - let mut fs_setup = - FarmStakingSetup::new(farm_staking::contract_obj, energy_factory::contract_obj); + let mut fs_setup = FarmStakingSetup::new( + farm_staking::contract_obj, + energy_factory::contract_obj, + permissions_hub::contract_obj, + ); let user_address = fs_setup.user_address.clone(); @@ -74,8 +80,11 @@ fn farm_staking_boosted_rewards_no_energy_test() { #[test] fn farm_staking_other_user_enter_negative_test() { DebugApi::dummy(); - let mut fs_setup = - FarmStakingSetup::new(farm_staking::contract_obj, energy_factory::contract_obj); + let mut fs_setup = FarmStakingSetup::new( + farm_staking::contract_obj, + energy_factory::contract_obj, + permissions_hub::contract_obj, + ); let user_address = fs_setup.user_address.clone(); let rand_user = fs_setup.b_mock.create_user_account(&rust_biguint!(0)); @@ -120,8 +129,11 @@ fn farm_staking_other_user_enter_negative_test() { #[test] fn farm_staking_boosted_rewards_with_energy_test() { DebugApi::dummy(); - let mut fs_setup = - FarmStakingSetup::new(farm_staking::contract_obj, energy_factory::contract_obj); + let mut fs_setup = FarmStakingSetup::new( + farm_staking::contract_obj, + energy_factory::contract_obj, + permissions_hub::contract_obj, + ); let user_address = fs_setup.user_address.clone(); let user_address2 = fs_setup.user_address2.clone(); @@ -345,8 +357,11 @@ fn farm_staking_boosted_rewards_with_energy_test() { #[test] fn farm_staking_partial_position_handling_test() { DebugApi::dummy(); - let mut fs_setup = - FarmStakingSetup::new(farm_staking::contract_obj, energy_factory::contract_obj); + let mut fs_setup = FarmStakingSetup::new( + farm_staking::contract_obj, + energy_factory::contract_obj, + permissions_hub::contract_obj, + ); let user_address = fs_setup.user_address.clone(); @@ -507,8 +522,11 @@ fn farm_staking_partial_position_handling_test() { #[test] fn farm_staking_claim_boosted_rewards_for_user_test() { DebugApi::dummy(); - let mut fs_setup = - FarmStakingSetup::new(farm_staking::contract_obj, energy_factory::contract_obj); + let mut fs_setup = FarmStakingSetup::new( + farm_staking::contract_obj, + energy_factory::contract_obj, + permissions_hub::contract_obj, + ); let user_address = fs_setup.user_address.clone(); @@ -621,8 +639,11 @@ fn farm_staking_claim_boosted_rewards_for_user_test() { #[test] fn farm_staking_full_position_boosted_rewards_test() { DebugApi::dummy(); - let mut fs_setup = - FarmStakingSetup::new(farm_staking::contract_obj, energy_factory::contract_obj); + let mut fs_setup = FarmStakingSetup::new( + farm_staking::contract_obj, + energy_factory::contract_obj, + permissions_hub::contract_obj, + ); let user_address = fs_setup.user_address.clone(); @@ -736,8 +757,11 @@ fn farm_staking_full_position_boosted_rewards_test() { #[test] fn position_owner_change_test() { DebugApi::dummy(); - let mut fs_setup = - FarmStakingSetup::new(farm_staking::contract_obj, energy_factory::contract_obj); + let mut fs_setup = FarmStakingSetup::new( + farm_staking::contract_obj, + energy_factory::contract_obj, + permissions_hub::contract_obj, + ); let first_user = fs_setup.user_address.clone(); let second_user = fs_setup.user_address2.clone(); @@ -1028,8 +1052,11 @@ fn position_owner_change_test() { #[test] fn farm_staking_farm_position_migration_test() { DebugApi::dummy(); - let mut fs_setup = - FarmStakingSetup::new(farm_staking::contract_obj, energy_factory::contract_obj); + let mut fs_setup = FarmStakingSetup::new( + farm_staking::contract_obj, + energy_factory::contract_obj, + permissions_hub::contract_obj, + ); let user = fs_setup.user_address.clone(); @@ -1122,8 +1149,11 @@ fn farm_staking_farm_position_migration_test() { #[test] fn boosted_rewards_config_change_test() { DebugApi::dummy(); - let mut fs_setup = - FarmStakingSetup::new(farm_staking::contract_obj, energy_factory::contract_obj); + let mut fs_setup = FarmStakingSetup::new( + farm_staking::contract_obj, + energy_factory::contract_obj, + permissions_hub::contract_obj, + ); let first_user = fs_setup.user_address.clone(); let second_user = fs_setup.user_address2.clone(); @@ -1432,8 +1462,11 @@ fn boosted_rewards_config_change_test() { #[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); + let mut fs_setup = FarmStakingSetup::new( + farm_staking::contract_obj, + energy_factory::contract_obj, + permissions_hub::contract_obj, + ); fs_setup.set_boosted_yields_factors(); fs_setup.set_boosted_yields_rewards_percentage(BOOSTED_YIELDS_PERCENTAGE); @@ -1527,8 +1560,11 @@ fn claim_only_boosted_rewards_per_week_test() { #[test] fn claim_rewards_per_week_test() { DebugApi::dummy(); - let mut fs_setup = - FarmStakingSetup::new(farm_staking::contract_obj, energy_factory::contract_obj); + let mut fs_setup = FarmStakingSetup::new( + farm_staking::contract_obj, + energy_factory::contract_obj, + permissions_hub::contract_obj, + ); fs_setup.set_boosted_yields_factors(); fs_setup.set_boosted_yields_rewards_percentage(BOOSTED_YIELDS_PERCENTAGE); @@ -1621,8 +1657,11 @@ fn claim_rewards_per_week_test() { #[test] fn claim_boosted_rewards_with_zero_position_test() { DebugApi::dummy(); - let mut fs_setup = - FarmStakingSetup::new(farm_staking::contract_obj, energy_factory::contract_obj); + let mut fs_setup = FarmStakingSetup::new( + farm_staking::contract_obj, + energy_factory::contract_obj, + permissions_hub::contract_obj, + ); fs_setup.set_boosted_yields_factors(); fs_setup.set_boosted_yields_rewards_percentage(BOOSTED_YIELDS_PERCENTAGE); @@ -1717,3 +1756,115 @@ fn claim_boosted_rewards_with_zero_position_test() { Some(&expected_attributes), ); } + +#[test] +fn test_multiple_positions_on_behalf() { + DebugApi::dummy(); + + let mut fs_setup = FarmStakingSetup::new( + farm_staking::contract_obj, + energy_factory::contract_obj, + permissions_hub::contract_obj, + ); + + fs_setup.set_boosted_yields_rewards_percentage(BOOSTED_YIELDS_PERCENTAGE); + fs_setup.set_boosted_yields_factors(); + let mut block_nonce = 0u64; + fs_setup.b_mock.set_block_nonce(block_nonce); + + // new external user + let external_user = fs_setup.b_mock.create_user_account(&rust_biguint!(0)); + fs_setup.set_user_energy(&external_user, 1_000, 1, 1); + + // authorized address + let farm_token_amount = 100_000_000; + let authorized_address = fs_setup.user_address.clone(); + fs_setup.b_mock.set_esdt_balance( + &authorized_address, + FARMING_TOKEN_ID, + &rust_biguint!(farm_token_amount * 2), + ); + + fs_setup.whitelist_address_on_behalf(&external_user, &authorized_address); + + fs_setup.check_farm_token_supply(0); + fs_setup.stake_farm_on_behalf(&authorized_address, &external_user, farm_token_amount, 0, 0); + fs_setup.check_farm_token_supply(farm_token_amount); + + let block_nonce_diff = 10u64; + block_nonce += block_nonce_diff; + fs_setup.b_mock.set_block_nonce(block_nonce); + + let base_rewards = 30u64; + let boosted_rewards = 10u64; + let total_rewards = base_rewards + boosted_rewards; + + // Only base rewards are given + fs_setup + .b_mock + .check_esdt_balance(&external_user, REWARD_TOKEN_ID, &rust_biguint!(0)); + fs_setup.claim_rewards_on_behalf(&authorized_address, 1, farm_token_amount); + fs_setup.b_mock.check_esdt_balance( + &external_user, + REWARD_TOKEN_ID, + &rust_biguint!(base_rewards), + ); + + // random tx on end of week 1, to cummulate rewards + fs_setup.b_mock.set_block_epoch(6); + let temp_user = fs_setup.b_mock.create_user_account(&rust_biguint!(0)); + fs_setup.b_mock.set_esdt_balance( + &temp_user, + FARMING_TOKEN_ID, + &rust_biguint!(USER_TOTAL_RIDE_TOKENS), + ); + fs_setup.set_user_energy(&external_user, 1_000, 6, 1); + fs_setup.set_user_energy(&temp_user, 1, 6, 1); + fs_setup.stake_farm(&temp_user, 10, &[], 3, 300_000u64, 0); + fs_setup.unstake_farm_no_checks(&temp_user, 10, 3); + + // advance 1 week + block_nonce += block_nonce_diff; + fs_setup.b_mock.set_block_nonce(block_nonce); + fs_setup.b_mock.set_block_epoch(10); + fs_setup.set_user_energy(&external_user, 1_000, 10, 1); + + // enter farm again for the same user (with additional payment) + fs_setup.check_farm_token_supply(farm_token_amount); + fs_setup.stake_farm_on_behalf( + &authorized_address, + &external_user, + farm_token_amount, + 2, // nonce 2 as the user already claimed with this position + farm_token_amount, + ); + fs_setup.check_farm_token_supply(farm_token_amount * 2); + fs_setup.b_mock.check_esdt_balance( + &external_user, + REWARD_TOKEN_ID, + &rust_biguint!(base_rewards + boosted_rewards), + ); + + fs_setup.claim_rewards_on_behalf(&authorized_address, 5, farm_token_amount * 2); + fs_setup.check_farm_token_supply(farm_token_amount * 2); + fs_setup.b_mock.check_esdt_balance( + &external_user, + REWARD_TOKEN_ID, + &rust_biguint!(total_rewards + base_rewards), + ); + + let farm_token_attributes: StakingFarmTokenAttributes = StakingFarmTokenAttributes { + reward_per_share: managed_biguint!(600_000u64), + compounded_reward: managed_biguint!(0), + current_farm_amount: managed_biguint!(farm_token_amount * 2), + original_owner: managed_address!(&external_user), + }; + + fs_setup.b_mock.check_nft_balance( + &authorized_address, + FARM_TOKEN_ID, + 6, + &rust_biguint!(farm_token_amount * 2), + Some(&farm_token_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 a27b2d0d0..bf732da74 100644 --- a/farm-staking/farm-staking/tests/farm_staking_setup/mod.rs +++ b/farm-staking/farm-staking/tests/farm_staking_setup/mod.rs @@ -1,5 +1,6 @@ #![allow(deprecated)] +use external_interaction::ExternalInteractionsModule; use farm_staking::claim_only_boosted_staking_rewards::ClaimOnlyBoostedStakingRewardsModule; use farm_staking::compound_stake_farm_rewards::CompoundStakeFarmRewardsModule; use multiversx_sc::codec::multi_types::OptionalValue; @@ -25,6 +26,7 @@ use farm_staking::unstake_farm::UnstakeFarmModule; use farm_staking::*; use farm_token::FarmTokenModule; use pausable::{PausableModule, State}; +use permissions_hub::PermissionsHub; use rewards::RewardsModule; pub static REWARD_TOKEN_ID: &[u8] = b"RIDE-abcdef"; // reward token ID @@ -38,6 +40,7 @@ pub const TOTAL_REWARDS_AMOUNT: u64 = 1_000_000_000_000; pub const USER_TOTAL_RIDE_TOKENS: u64 = 5_000_000_000; +pub const MAX_PERCENTAGE: u64 = 10_000; // 100% pub const BOOSTED_YIELDS_PERCENTAGE: u64 = 2_500; // 25% pub const MAX_REWARDS_FACTOR: u64 = 10; pub const USER_REWARDS_ENERGY_CONST: u64 = 3; @@ -52,10 +55,11 @@ pub struct NonceAmountPair { pub amount: u64, } -pub struct FarmStakingSetup +pub struct FarmStakingSetup where FarmObjBuilder: 'static + Copy + Fn() -> farm_staking::ContractObj, EnergyFactoryBuilder: 'static + Copy + Fn() -> energy_factory::ContractObj, + PermissionsHubObjBuilder: 'static + Copy + Fn() -> permissions_hub::ContractObj, { pub b_mock: BlockchainStateWrapper, pub owner_address: Address, @@ -64,14 +68,22 @@ where pub farm_wrapper: ContractObjWrapper, FarmObjBuilder>, pub energy_factory_wrapper: ContractObjWrapper, EnergyFactoryBuilder>, + pub permissions_hub_wrapper: + ContractObjWrapper, PermissionsHubObjBuilder>, } -impl FarmStakingSetup +impl + FarmStakingSetup where FarmObjBuilder: 'static + Copy + Fn() -> farm_staking::ContractObj, EnergyFactoryBuilder: 'static + Copy + Fn() -> energy_factory::ContractObj, + PermissionsHubObjBuilder: 'static + Copy + Fn() -> permissions_hub::ContractObj, { - pub fn new(farm_builder: FarmObjBuilder, energy_factory_builder: EnergyFactoryBuilder) -> Self { + pub fn new( + farm_builder: FarmObjBuilder, + energy_factory_builder: EnergyFactoryBuilder, + permissions_hub_builder: PermissionsHubObjBuilder, + ) -> Self { let rust_zero = rust_biguint!(0u64); let mut b_mock = BlockchainStateWrapper::new(); let owner_addr = b_mock.create_user_account(&rust_zero); @@ -85,6 +97,19 @@ where "energy_factory.wasm", ); + let permissions_hub_wrapper = b_mock.create_sc_account( + &rust_zero, + Some(&owner_addr), + permissions_hub_builder, + "permissions_hub.wasm", + ); + + b_mock + .execute_tx(&owner_addr, &permissions_hub_wrapper, &rust_zero, |sc| { + sc.init(); + }) + .assert_ok(); + // init farm contract b_mock @@ -112,6 +137,10 @@ where sc.energy_factory_address() .set(managed_address!(energy_factory_wrapper.address_ref())); + + sc.set_permissions_hub_address(managed_address!( + permissions_hub_wrapper.address_ref() + )); }) .assert_ok(); @@ -167,6 +196,7 @@ where user_address2: user_addr2, farm_wrapper, energy_factory_wrapper, + permissions_hub_wrapper, } } @@ -566,6 +596,95 @@ where ); } + pub fn stake_farm_on_behalf( + &mut self, + caller: &Address, + user: &Address, + farming_token_amount: u64, + farm_token_nonce: u64, + farm_token_amount: u64, + ) { + let mut payments = Vec::new(); + payments.push(TxTokenTransfer { + token_identifier: FARMING_TOKEN_ID.to_vec(), + nonce: 0, + value: rust_biguint!(farming_token_amount), + }); + + if farm_token_nonce > 0 { + payments.push(TxTokenTransfer { + token_identifier: FARM_TOKEN_ID.to_vec(), + nonce: farm_token_nonce, + value: rust_biguint!(farm_token_amount), + }); + } + + let b_mock = &mut self.b_mock; + b_mock + .execute_esdt_multi_transfer(caller, &self.farm_wrapper, &payments, |sc| { + let stake_farm_result = sc.stake_farm_on_behalf(managed_address!(user)); + let (out_farm_token, _reward_token) = stake_farm_result.into_tuple(); + assert_eq!( + out_farm_token.token_identifier, + managed_token_id!(FARM_TOKEN_ID) + ); + assert_eq!( + out_farm_token.amount, + managed_biguint!(farming_token_amount + farm_token_amount) + ); + }) + .assert_ok(); + } + + pub fn claim_rewards_on_behalf( + &mut self, + caller: &Address, + farm_token_nonce: u64, + farm_token_amount: u64, + ) -> u64 { + let mut result = 0; + self.b_mock + .execute_esdt_transfer( + caller, + &self.farm_wrapper, + FARM_TOKEN_ID, + farm_token_nonce, + &rust_biguint!(farm_token_amount), + |sc| { + let (out_farm_token, out_reward_token) = + sc.claim_rewards_on_behalf().into_tuple(); + assert_eq!( + out_farm_token.token_identifier, + managed_token_id!(FARM_TOKEN_ID) + ); + assert_eq!(out_farm_token.amount, managed_biguint!(farm_token_amount)); + + assert_eq!( + out_reward_token.token_identifier, + managed_token_id!(REWARD_TOKEN_ID) + ); + + result = out_reward_token.amount.to_u64().unwrap(); + }, + ) + .assert_ok(); + + result + } + + pub fn whitelist_address_on_behalf(&mut self, user: &Address, address_to_whitelist: &Address) { + self.b_mock + .execute_tx( + user, + &self.permissions_hub_wrapper, + &rust_biguint!(0), + |sc| { + sc.whitelist(managed_address!(address_to_whitelist)); + }, + ) + .assert_ok(); + } + pub fn check_farm_token_supply(&mut self, expected_farm_token_supply: u64) { self.b_mock .execute_query(&self.farm_wrapper, |sc| { diff --git a/farm-staking/farm-staking/tests/farm_staking_test.rs b/farm-staking/farm-staking/tests/farm_staking_test.rs index 26a9d065b..24ab2bef7 100644 --- a/farm-staking/farm-staking/tests/farm_staking_test.rs +++ b/farm-staking/farm-staking/tests/farm_staking_test.rs @@ -11,14 +11,21 @@ use farm_staking_setup::*; #[test] fn test_farm_setup() { - let _ = FarmStakingSetup::new(farm_staking::contract_obj, energy_factory::contract_obj); + let _ = FarmStakingSetup::new( + farm_staking::contract_obj, + energy_factory::contract_obj, + permissions_hub::contract_obj, + ); } #[test] fn test_enter_farm() { DebugApi::dummy(); - let mut farm_setup = - FarmStakingSetup::new(farm_staking::contract_obj, energy_factory::contract_obj); + let mut farm_setup = FarmStakingSetup::new( + farm_staking::contract_obj, + energy_factory::contract_obj, + permissions_hub::contract_obj, + ); let user_address = farm_setup.user_address.clone(); @@ -38,8 +45,11 @@ fn test_enter_farm() { #[test] fn test_unstake_farm() { DebugApi::dummy(); - let mut farm_setup = - FarmStakingSetup::new(farm_staking::contract_obj, energy_factory::contract_obj); + let mut farm_setup = FarmStakingSetup::new( + farm_staking::contract_obj, + energy_factory::contract_obj, + permissions_hub::contract_obj, + ); let user_address = farm_setup.user_address.clone(); @@ -90,8 +100,11 @@ fn test_unstake_farm() { #[test] fn test_claim_rewards() { DebugApi::dummy(); - let mut farm_setup = - FarmStakingSetup::new(farm_staking::contract_obj, energy_factory::contract_obj); + let mut farm_setup = FarmStakingSetup::new( + farm_staking::contract_obj, + energy_factory::contract_obj, + permissions_hub::contract_obj, + ); let user_address = farm_setup.user_address.clone(); @@ -128,15 +141,21 @@ fn test_claim_rewards() { farm_setup.check_farm_token_supply(farm_in_amount); } -fn steps_enter_farm_twice( +fn steps_enter_farm_twice( farm_builder: FarmObjBuilder, energy_factory_builder: EnergyFactoryBuilder, -) -> FarmStakingSetup + permissions_hub_builder: PermissionsHubObjBuilder, +) -> FarmStakingSetup where FarmObjBuilder: 'static + Copy + Fn() -> farm_staking::ContractObj, EnergyFactoryBuilder: 'static + Copy + Fn() -> energy_factory::ContractObj, + PermissionsHubObjBuilder: 'static + Copy + Fn() -> permissions_hub::ContractObj, { - let mut farm_setup = FarmStakingSetup::new(farm_builder, energy_factory_builder); + let mut farm_setup = FarmStakingSetup::new( + farm_builder, + energy_factory_builder, + permissions_hub_builder, + ); let user_address = farm_setup.user_address.clone(); @@ -187,14 +206,21 @@ where #[test] fn test_enter_farm_twice() { DebugApi::dummy(); - let _ = steps_enter_farm_twice(farm_staking::contract_obj, energy_factory::contract_obj); + let _ = steps_enter_farm_twice( + farm_staking::contract_obj, + energy_factory::contract_obj, + permissions_hub::contract_obj, + ); } #[test] fn test_exit_farm_after_enter_twice() { DebugApi::dummy(); - let mut farm_setup = - steps_enter_farm_twice(farm_staking::contract_obj, energy_factory::contract_obj); + let mut farm_setup = steps_enter_farm_twice( + farm_staking::contract_obj, + energy_factory::contract_obj, + permissions_hub::contract_obj, + ); let user_address = farm_setup.user_address.clone(); @@ -227,8 +253,11 @@ fn test_exit_farm_after_enter_twice() { #[test] fn test_unbond() { DebugApi::dummy(); - let mut farm_setup = - FarmStakingSetup::new(farm_staking::contract_obj, energy_factory::contract_obj); + let mut farm_setup = FarmStakingSetup::new( + farm_staking::contract_obj, + energy_factory::contract_obj, + permissions_hub::contract_obj, + ); let user_address = farm_setup.user_address.clone(); @@ -288,8 +317,11 @@ fn test_unbond() { #[test] fn test_withdraw_rewards() { DebugApi::dummy(); - let mut farm_setup = - FarmStakingSetup::new(farm_staking::contract_obj, energy_factory::contract_obj); + let mut farm_setup = FarmStakingSetup::new( + farm_staking::contract_obj, + energy_factory::contract_obj, + permissions_hub::contract_obj, + ); let initial_rewards_capacity = 1_000_000_000_000u64; farm_setup.check_rewards_capacity(initial_rewards_capacity); @@ -304,8 +336,11 @@ fn test_withdraw_rewards() { #[test] fn test_withdraw_after_produced_rewards() { DebugApi::dummy(); - let mut farm_setup = - FarmStakingSetup::new(farm_staking::contract_obj, energy_factory::contract_obj); + let mut farm_setup = FarmStakingSetup::new( + farm_staking::contract_obj, + energy_factory::contract_obj, + permissions_hub::contract_obj, + ); let user_address = farm_setup.user_address.clone();