diff --git a/Cargo.lock b/Cargo.lock index ad010ebcc..5d3ca5085 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -889,12 +889,22 @@ name = "farm-staking-proxy-legacy" version = "0.0.0" dependencies = [ "common_structs", + "energy-factory", + "energy-query", + "farm-staking", + "farm-with-locked-rewards", + "farm_token", "hex", + "locking_module", "multiversx-sc", "multiversx-sc-modules", "multiversx-sc-scenario", "num-bigint", "num-traits", + "pair", + "pausable", + "sc_whitelist_module", + "simple-lock", "token_merge_helper", ] diff --git a/legacy-contracts/farm-staking-proxy-legacy/Cargo.toml b/legacy-contracts/farm-staking-proxy-legacy/Cargo.toml index 8804de4a4..71a633eec 100644 --- a/legacy-contracts/farm-staking-proxy-legacy/Cargo.toml +++ b/legacy-contracts/farm-staking-proxy-legacy/Cargo.toml @@ -21,6 +21,36 @@ path = "../../common/modules/token_merge_helper" [dependencies.common_structs] path = "../../common/common_structs" +[dependencies.farm-staking] +path = "../../farm-staking/farm-staking" + +[dependencies.pair] +path = "../../dex/pair" + +[dependencies.farm-with-locked-rewards] +path = "../../dex/farm-with-locked-rewards" + +[dev-dependencies.energy-factory] +path = "../../locked-asset/energy-factory" + +[dev-dependencies.energy-query] +path = "../../energy-integration/common-modules/energy-query" + +[dev-dependencies.simple-lock] +path = "../../locked-asset/simple-lock" + +[dev-dependencies.farm_token] +path = "../../common/modules/farm/farm_token" + +[dev-dependencies.locking_module] +path = "../../common/modules/locking_module" + +[dev-dependencies.pausable] +path = "../../common/modules/pausable" + +[dev-dependencies.sc_whitelist_module] +path = "../../common/modules/sc_whitelist_module" + [dev-dependencies] num-bigint = "0.4.2" num-traits = "0.2" diff --git a/legacy-contracts/farm-staking-proxy-legacy/src/external_contracts_interactions.rs b/legacy-contracts/farm-staking-proxy-legacy/src/external_contracts_interactions.rs index a6826771b..3c4c92000 100644 --- a/legacy-contracts/farm-staking-proxy-legacy/src/external_contracts_interactions.rs +++ b/legacy-contracts/farm-staking-proxy-legacy/src/external_contracts_interactions.rs @@ -1,57 +1,13 @@ multiversx_sc::imports!(); use common_structs::{RawResultWrapper, RawResultsType}; +use farm_staking::unstake_farm::ProxyTrait as _; +use multiversx_sc::storage::StorageKey; +use pair::{pair_actions::remove_liq::ProxyTrait as _, safe_price_view::ProxyTrait as _}; use crate::result_types::*; -mod farm_proxy { - multiversx_sc::imports!(); - - #[multiversx_sc::proxy] - pub trait FarmProxy { - #[payable("*")] - #[endpoint(exitFarm)] - fn exit_farm(&self) -> MultiValue2; - - #[view(getFarmingTokenId)] - fn farming_token_id(&self) -> TokenIdentifier; - } -} - -mod farm_staking_proxy { - multiversx_sc::imports!(); - - #[multiversx_sc::proxy] - pub trait FarmStakingProxy { - #[payable("*")] - #[endpoint(unstakeFarmThroughProxy)] - fn unstake_farm_through_proxy( - &self, - original_caller: ManagedAddress, - ) -> MultiValue2; - } -} - -mod pair_proxy { - multiversx_sc::imports!(); - - #[multiversx_sc::proxy] - pub trait PairProxy { - #[payable("*")] - #[endpoint(removeLiquidity)] - fn remove_liquidity( - &self, - first_token_amount_min: BigUint, - second_token_amount_min: BigUint, - ) -> MultiValue2; - - #[endpoint(updateAndGetTokensForGivenPositionWithSafePrice)] - fn update_and_get_tokens_for_given_position_with_safe_price( - &self, - liquidity: BigUint, - ) -> MultiValue2; - } -} +pub static FARMING_TOKEN_STORAGE_KEY: &[u8] = b"farming_token_id"; #[multiversx_sc::module] pub trait ExternalContractsInteractionsModule: @@ -61,6 +17,7 @@ pub trait ExternalContractsInteractionsModule: fn lp_farm_exit( &self, + orig_caller: ManagedAddress, lp_farm_token_nonce: u64, lp_farm_token_amount: BigUint, ) -> LpFarmExitResult { @@ -68,7 +25,7 @@ pub trait ExternalContractsInteractionsModule: let lp_farm_address = self.lp_farm_address().get(); let raw_results: RawResultsType = self .lp_farm_proxy_obj(lp_farm_address) - .exit_farm() + .exit_farm_endpoint(OptionalValue::Some(orig_caller)) .with_esdt_transfer((lp_farm_token_id, lp_farm_token_nonce, lp_farm_token_amount)) .execute_on_dest_context(); @@ -93,9 +50,13 @@ pub trait ExternalContractsInteractionsModule: fn get_lp_farming_token_identifier(&self) -> TokenIdentifier { let lp_farm_address = self.lp_farm_address().get(); - self.lp_farm_proxy_obj(lp_farm_address) - .farming_token_id() - .execute_on_dest_context() + + let farming_token_mapper = SingleValueMapper::<_, _, ManagedAddress>::new_from_address( + lp_farm_address, + StorageKey::new(FARMING_TOKEN_STORAGE_KEY), + ); + + farming_token_mapper.get() } // staking farm @@ -201,16 +162,16 @@ pub trait ExternalContractsInteractionsModule: // proxies #[proxy] - fn staking_farm_proxy_obj( - &self, - sc_address: ManagedAddress, - ) -> farm_staking_proxy::Proxy; + fn staking_farm_proxy_obj(&self, sc_address: ManagedAddress) -> farm_staking::Proxy; #[proxy] - fn lp_farm_proxy_obj(&self, sc_address: ManagedAddress) -> farm_proxy::Proxy; + fn lp_farm_proxy_obj( + &self, + sc_address: ManagedAddress, + ) -> farm_with_locked_rewards::Proxy; #[proxy] - fn pair_proxy_obj(&self, sc_address: ManagedAddress) -> pair_proxy::Proxy; + fn pair_proxy_obj(&self, sc_address: ManagedAddress) -> pair::Proxy; // storage diff --git a/legacy-contracts/farm-staking-proxy-legacy/src/lib.rs b/legacy-contracts/farm-staking-proxy-legacy/src/lib.rs index 957285429..211e4ec0e 100644 --- a/legacy-contracts/farm-staking-proxy-legacy/src/lib.rs +++ b/legacy-contracts/farm-staking-proxy-legacy/src/lib.rs @@ -34,8 +34,11 @@ pub trait FarmStakingProxy: let attributes = self.get_dual_yield_token_attributes(payment_nonce); let lp_farm_token_amount = self.get_lp_farm_token_amount_equivalent(&attributes, &payment_amount); - let lp_farm_exit_result = - self.lp_farm_exit(attributes.lp_farm_token_nonce, lp_farm_token_amount); + let lp_farm_exit_result = self.lp_farm_exit( + caller.clone(), + attributes.lp_farm_token_nonce, + lp_farm_token_amount, + ); let remove_liq_result = self.pair_remove_liquidity( lp_farm_exit_result.lp_tokens, diff --git a/legacy-contracts/farm-staking-proxy-legacy/tests/constants/mod.rs b/legacy-contracts/farm-staking-proxy-legacy/tests/constants/mod.rs new file mode 100644 index 000000000..b9eef5056 --- /dev/null +++ b/legacy-contracts/farm-staking-proxy-legacy/tests/constants/mod.rs @@ -0,0 +1,48 @@ +// Pair constants + +pub const PAIR_WASM_PATH: &'static str = "pair/output/pair.wasm"; +pub const WEGLD_TOKEN_ID: &[u8] = b"WEGLD-abcdef"; +pub const RIDE_TOKEN_ID: &[u8] = b"RIDE-abcdef"; +pub const LP_TOKEN_ID: &[u8] = b"LPTOK-abcdef"; // also farming token ID for LP farm + +pub const USER_TOTAL_WEGLD_TOKENS: u64 = 2_000_000_000; +pub const USER_TOTAL_RIDE_TOKENS: u64 = 2_000_000_000; +pub const USER_TOTAL_LP_TOKENS: u64 = 1_001_000_000; + +pub const BLOCK_NONCE_FIRST_ADD_LIQ: u64 = 5; +pub const BLOCK_NONCE_SECOND_ADD_LIQ: u64 = 6; +pub const BLOCK_NONCE_AFTER_PAIR_SETUP: u64 = 100; + +// LP farm constants + +pub const FARM_WASM_PATH: &'static str = "farm/output/farm.wasm"; +pub const LP_FARM_TOKEN_ID: &[u8] = b"LPFARM-abcdef"; +pub const DIVISION_SAFETY_CONSTANT: u64 = 1_000_000_000_000; +pub const MIN_FARMING_EPOCHS: u8 = 2; +pub const PENALTY_PERCENT: u64 = 10; +pub const LP_FARM_PER_BLOCK_REWARD_AMOUNT: u64 = 5_000; + +// Energy factory constants + +pub const EPOCHS_IN_YEAR: u64 = 360; +pub static MEX_TOKEN_ID: &[u8] = b"MEX-123456"; +pub static LOCKED_TOKEN_ID: &[u8] = b"LOCKED-123456"; +pub static LEGACY_LOCKED_TOKEN_ID: &[u8] = b"LEGACY-123456"; +pub static LOCK_OPTIONS: &[u64] = &[EPOCHS_IN_YEAR, 5 * EPOCHS_IN_YEAR, 10 * EPOCHS_IN_YEAR]; // 1, 5 or 10 years +pub static PENALTY_PERCENTAGES: &[u64] = &[4_000, 6_000, 8_000]; + +// Staking farm constants + +pub const STAKING_FARM_WASM_PATH: &str = "farm-staking/output/farm-staking.wasm"; +pub const STAKING_REWARD_TOKEN_ID: &[u8] = RIDE_TOKEN_ID; +pub const STAKING_TOKEN_ID: &[u8] = RIDE_TOKEN_ID; +pub const STAKING_FARM_TOKEN_ID: &[u8] = b"STKFARM-abcdef"; +pub const MAX_APR: u64 = 5_000; // 50% +pub const UNBOND_EPOCHS: u64 = 10; +pub const STAKING_FARM_PER_BLOCK_REWARD_AMOUNT: u64 = 1_000; +pub const REWARD_CAPACITY: u64 = 1_000_000_000_000; + +// Proxy constants + +pub const PROXY_WASM_PATH: &str = "farm-staking-proxy/output/farm-staking-proxy"; +pub const DUAL_YIELD_TOKEN_ID: &[u8] = b"DYIELD-abcdef"; diff --git a/legacy-contracts/farm-staking-proxy-legacy/tests/staking_farm_with_lp.rs b/legacy-contracts/farm-staking-proxy-legacy/tests/staking_farm_with_lp.rs new file mode 100644 index 000000000..c7f92adf0 --- /dev/null +++ b/legacy-contracts/farm-staking-proxy-legacy/tests/staking_farm_with_lp.rs @@ -0,0 +1,149 @@ +#![allow(deprecated)] + +pub mod constants; +pub mod staking_farm_with_lp_external_contracts; +pub mod staking_farm_with_lp_staking_contract_interactions; +pub mod staking_farm_with_lp_staking_contract_setup; + +multiversx_sc::imports!(); + +use common_structs::FarmTokenAttributes; +use constants::*; +use farm_staking_proxy_legacy::dual_yield_token::DualYieldTokenAttributes; + +use farm_staking::stake_farm::StakeFarmModule; +use farm_with_locked_rewards::Farm; +use multiversx_sc_scenario::{ + imports::TxTokenTransfer, managed_address, managed_biguint, rust_biguint, DebugApi, +}; +use pair::pair_actions::add_liq::AddLiquidityModule; +use staking_farm_with_lp_staking_contract_interactions::*; + +#[test] +fn test_all_setup() { + let _ = FarmStakingSetup::new( + pair::contract_obj, + farm_with_locked_rewards::contract_obj, + energy_factory::contract_obj, + farm_staking::contract_obj, + farm_staking_proxy_legacy::contract_obj, + ); +} + +#[test] +fn test_unstake_from_legacy_proxy() { + let mut setup = FarmStakingSetup::new( + pair::contract_obj, + farm_with_locked_rewards::contract_obj, + energy_factory::contract_obj, + farm_staking::contract_obj, + farm_staking_proxy_legacy::contract_obj, + ); + + DebugApi::dummy(); + setup + .b_mock + .set_block_nonce(BLOCK_NONCE_AFTER_PAIR_SETUP + 20); + setup.b_mock.set_block_epoch(20); + + let token_amount = 1_000_000_000u64; + + let payments = vec![ + TxTokenTransfer { + token_identifier: WEGLD_TOKEN_ID.to_vec(), + nonce: 0, + value: rust_biguint!(token_amount), + }, + TxTokenTransfer { + token_identifier: RIDE_TOKEN_ID.to_vec(), + nonce: 0, + value: rust_biguint!(token_amount), + }, + ]; + setup + .b_mock + .execute_esdt_multi_transfer(&setup.user_addr, &setup.pair_wrapper, &payments, |sc| { + sc.add_liquidity(managed_biguint!(1u64), managed_biguint!(1u64)); + }) + .assert_ok(); + + setup + .b_mock + .execute_esdt_transfer( + &setup.user_addr, + &setup.lp_farm_wrapper, + LP_TOKEN_ID, + 0, + &rust_biguint!(token_amount), + |sc| { + sc.enter_farm_endpoint(OptionalValue::None); + }, + ) + .assert_ok(); + + // Simulate enter proxy staking contract + let lp_farm_token_attributes: FarmTokenAttributes = FarmTokenAttributes { + reward_per_share: managed_biguint!(0), + entering_epoch: 20, + compounded_reward: managed_biguint!(0), + current_farm_amount: managed_biguint!(token_amount), + original_owner: managed_address!(&setup.user_addr), + }; + setup.b_mock.set_nft_balance( + setup.proxy_wrapper.address_ref(), + LP_FARM_TOKEN_ID, + 1, + &rust_biguint!(token_amount), + &lp_farm_token_attributes, + ); + setup.b_mock.set_esdt_balance( + setup.proxy_wrapper.address_ref(), + RIDE_TOKEN_ID, + &rust_biguint!(token_amount), + ); + + setup + .b_mock + .execute_tx( + setup.proxy_wrapper.address_ref(), + &setup.staking_farm_wrapper, + &rust_biguint!(0u64), + |sc| { + sc.stake_farm_through_proxy( + managed_biguint!(token_amount), + managed_address!(&setup.user_addr), + ); + }, + ) + .assert_ok(); + + let dual_yield_token_amount = token_amount; + let dual_yield_token_attributes: DualYieldTokenAttributes = + DualYieldTokenAttributes { + lp_farm_token_nonce: 1, + lp_farm_token_amount: managed_biguint!(dual_yield_token_amount), + staking_farm_token_nonce: 1, + staking_farm_token_amount: managed_biguint!(dual_yield_token_amount), + }; + setup.b_mock.set_nft_balance( + &setup.user_addr, + DUAL_YIELD_TOKEN_ID, + 1, + &rust_biguint!(dual_yield_token_amount), + &dual_yield_token_attributes, + ); + + let expected_token_amount = 990_000_000u64; + setup.unstake_proxy( + 1, + dual_yield_token_amount, + expected_token_amount, + 0, + 0, + expected_token_amount, + 30, + ); + + setup.b_mock.set_block_epoch(30); + setup.unbond_proxy(2, expected_token_amount, expected_token_amount); +} diff --git a/legacy-contracts/farm-staking-proxy-legacy/tests/staking_farm_with_lp_external_contracts/mod.rs b/legacy-contracts/farm-staking-proxy-legacy/tests/staking_farm_with_lp_external_contracts/mod.rs new file mode 100644 index 000000000..94306f44e --- /dev/null +++ b/legacy-contracts/farm-staking-proxy-legacy/tests/staking_farm_with_lp_external_contracts/mod.rs @@ -0,0 +1,253 @@ +use energy_factory::token_whitelist::TokenWhitelistModule; +use energy_factory::SimpleLockEnergy; +use energy_query::EnergyQueryModule; +use farm_token::FarmTokenModule; +use farm_with_locked_rewards::Farm; +use multiversx_sc::imports::StorageTokenWrapper; +use multiversx_sc::types::{Address, EsdtLocalRole, MultiValueEncoded}; +use multiversx_sc_modules::pause::PauseModule; +use multiversx_sc_scenario::{ + managed_address, managed_biguint, managed_token_id, rust_biguint, testing_framework::*, + DebugApi, +}; + +use pair::config as pair_config; +use pair::*; +use pair_actions::add_liq::AddLiquidityModule; +use pair_actions::initial_liq::InitialLiquidityModule; +use pair_config::ConfigModule as _; + +use pausable::{PausableModule, State}; +use simple_lock::locked_token::LockedTokenModule as _; + +use crate::constants::*; + +pub fn setup_pair( + owner_addr: &Address, + user_addr: &Address, + b_mock: &mut BlockchainStateWrapper, + pair_builder: PairObjBuilder, +) -> ContractObjWrapper, PairObjBuilder> +where + PairObjBuilder: 'static + Copy + Fn() -> pair::ContractObj, +{ + let rust_zero = rust_biguint!(0u64); + let pair_wrapper = + b_mock.create_sc_account(&rust_zero, Some(owner_addr), pair_builder, PAIR_WASM_PATH); + + b_mock + .execute_tx(&owner_addr, &pair_wrapper, &rust_zero, |sc| { + let first_token_id = managed_token_id!(WEGLD_TOKEN_ID); + let second_token_id = managed_token_id!(RIDE_TOKEN_ID); + let router_address = managed_address!(&owner_addr); + let router_owner_address = managed_address!(&owner_addr); + let initial_liquidity_adder = managed_address!(&owner_addr); + let total_fee_percent = 300u64; + let special_fee_percent = 50u64; + + sc.init( + first_token_id, + second_token_id, + router_address, + router_owner_address, + total_fee_percent, + special_fee_percent, + initial_liquidity_adder, + MultiValueEncoded::new(), + ); + + let lp_token_id = managed_token_id!(LP_TOKEN_ID); + sc.lp_token_identifier().set(&lp_token_id); + }) + .assert_ok(); + + let lp_token_roles = [EsdtLocalRole::Mint, EsdtLocalRole::Burn]; + b_mock.set_esdt_local_roles(pair_wrapper.address_ref(), LP_TOKEN_ID, &lp_token_roles[..]); + + // set user balance + b_mock.set_esdt_balance( + &user_addr, + WEGLD_TOKEN_ID, + &rust_biguint!(USER_TOTAL_WEGLD_TOKENS), + ); + b_mock.set_esdt_balance( + &user_addr, + RIDE_TOKEN_ID, + &rust_biguint!(USER_TOTAL_RIDE_TOKENS), + ); + + b_mock.set_block_nonce(BLOCK_NONCE_FIRST_ADD_LIQ); + + b_mock.set_esdt_balance( + &owner_addr, + WEGLD_TOKEN_ID, + &rust_biguint!(USER_TOTAL_WEGLD_TOKENS), + ); + b_mock.set_esdt_balance( + &owner_addr, + RIDE_TOKEN_ID, + &rust_biguint!(USER_TOTAL_RIDE_TOKENS), + ); + + let payments = vec![ + TxTokenTransfer { + token_identifier: WEGLD_TOKEN_ID.to_vec(), + nonce: 0, + value: rust_biguint!(1_000_000_000), + }, + TxTokenTransfer { + token_identifier: RIDE_TOKEN_ID.to_vec(), + nonce: 0, + value: rust_biguint!(1_000_000_000), + }, + ]; + b_mock + .execute_esdt_multi_transfer(&owner_addr, &pair_wrapper, &payments, |sc| { + sc.add_initial_liquidity(); + }) + .assert_ok(); + + b_mock.set_block_nonce(BLOCK_NONCE_SECOND_ADD_LIQ); + + b_mock + .execute_esdt_multi_transfer(&owner_addr, &pair_wrapper, &payments, |sc| { + sc.add_liquidity(managed_biguint!(1u64), managed_biguint!(1u64)); + }) + .assert_ok(); + + b_mock.set_block_nonce(BLOCK_NONCE_AFTER_PAIR_SETUP); + + pair_wrapper +} + +pub fn setup_lp_farm( + owner_addr: &Address, + energy_factory_address: &Address, + b_mock: &mut BlockchainStateWrapper, + farm_builder: FarmObjBuilder, +) -> ContractObjWrapper, FarmObjBuilder> +where + FarmObjBuilder: 'static + Copy + Fn() -> farm_with_locked_rewards::ContractObj, +{ + let rust_zero = rust_biguint!(0u64); + let farm_wrapper = + b_mock.create_sc_account(&rust_zero, Some(&owner_addr), farm_builder, FARM_WASM_PATH); + + // init farm contract + + b_mock + .execute_tx(&owner_addr, &farm_wrapper, &rust_zero, |sc| { + let reward_token_id = managed_token_id!(RIDE_TOKEN_ID); + let farming_token_id = managed_token_id!(LP_TOKEN_ID); + let division_safety_constant = managed_biguint!(DIVISION_SAFETY_CONSTANT); + let pair_address = managed_address!(&Address::zero()); + + sc.init( + reward_token_id, + farming_token_id, + division_safety_constant, + pair_address, + managed_address!(owner_addr), + MultiValueEncoded::new(), + ); + + let farm_token_id = managed_token_id!(LP_FARM_TOKEN_ID); + sc.farm_token().set_token_id(farm_token_id); + + sc.energy_factory_address() + .set(managed_address!(energy_factory_address)); + + sc.state().set(State::Active); + }) + .assert_ok(); + + let farm_token_roles = [ + EsdtLocalRole::NftCreate, + EsdtLocalRole::NftAddQuantity, + EsdtLocalRole::NftBurn, + ]; + b_mock.set_esdt_local_roles( + farm_wrapper.address_ref(), + LP_FARM_TOKEN_ID, + &farm_token_roles[..], + ); + + let farming_token_roles = [EsdtLocalRole::Burn]; + b_mock.set_esdt_local_roles( + farm_wrapper.address_ref(), + LP_TOKEN_ID, + &farming_token_roles[..], + ); + + let reward_token_roles = [EsdtLocalRole::Mint]; + b_mock.set_esdt_local_roles( + farm_wrapper.address_ref(), + RIDE_TOKEN_ID, + &reward_token_roles[..], + ); + + farm_wrapper +} + +pub fn setup_energy_factory( + owner_addr: &Address, + b_mock: &mut BlockchainStateWrapper, + energy_factory_builder: EnergyFactoryObjBuilder, +) -> ContractObjWrapper, EnergyFactoryObjBuilder> +where + EnergyFactoryObjBuilder: 'static + Copy + Fn() -> energy_factory::ContractObj, +{ + let rust_zero = rust_biguint!(0u64); + let energy_factory_wrapper = b_mock.create_sc_account( + &rust_zero, + Some(owner_addr), + energy_factory_builder, + "energy factory", + ); + + b_mock + .execute_tx(owner_addr, &energy_factory_wrapper, &rust_zero, |sc| { + let mut lock_options = MultiValueEncoded::new(); + for (option, penalty) in LOCK_OPTIONS.iter().zip(PENALTY_PERCENTAGES.iter()) { + lock_options.push((*option, *penalty).into()); + } + + sc.init( + managed_token_id!(LOCKED_TOKEN_ID), + managed_token_id!(LEGACY_LOCKED_TOKEN_ID), + managed_address!(energy_factory_wrapper.address_ref()), + 0, + lock_options, + ); + + sc.base_asset_token_id() + .set(managed_token_id!(MEX_TOKEN_ID)); + sc.locked_token() + .set_token_id(managed_token_id!(LOCKED_TOKEN_ID)); + sc.set_paused(false); + }) + .assert_ok(); + + b_mock.set_esdt_local_roles( + energy_factory_wrapper.address_ref(), + MEX_TOKEN_ID, + &[EsdtLocalRole::Mint, EsdtLocalRole::Burn], + ); + b_mock.set_esdt_local_roles( + energy_factory_wrapper.address_ref(), + LOCKED_TOKEN_ID, + &[ + EsdtLocalRole::NftCreate, + EsdtLocalRole::NftAddQuantity, + EsdtLocalRole::NftBurn, + EsdtLocalRole::Transfer, + ], + ); + b_mock.set_esdt_local_roles( + energy_factory_wrapper.address_ref(), + LEGACY_LOCKED_TOKEN_ID, + &[EsdtLocalRole::NftBurn], + ); + + energy_factory_wrapper +} diff --git a/legacy-contracts/farm-staking-proxy-legacy/tests/staking_farm_with_lp_staking_contract_interactions/mod.rs b/legacy-contracts/farm-staking-proxy-legacy/tests/staking_farm_with_lp_staking_contract_interactions/mod.rs new file mode 100644 index 000000000..cc134820c --- /dev/null +++ b/legacy-contracts/farm-staking-proxy-legacy/tests/staking_farm_with_lp_staking_contract_interactions/mod.rs @@ -0,0 +1,222 @@ +use multiversx_sc::types::Address; +use multiversx_sc_scenario::{ + managed_biguint, rust_biguint, + testing_framework::{BlockchainStateWrapper, ContractObjWrapper}, + DebugApi, +}; + +use farm_staking::*; +use farm_staking_proxy_legacy::*; +use token_attributes::UnbondSftAttributes; +use unbond_farm::UnbondFarmModule; + +use crate::{ + constants::*, + staking_farm_with_lp_external_contracts::{setup_energy_factory, setup_lp_farm, setup_pair}, + staking_farm_with_lp_staking_contract_setup::{ + add_proxy_to_whitelist, setup_proxy, setup_staking_farm, + }, +}; + +pub struct NonceAmountPair { + pub nonce: u64, + pub amount: u64, +} + +pub struct FarmStakingSetup< + PairObjBuilder, + FarmObjBuilder, + EnergyFactoryObjBuilder, + StakingContractObjBuilder, + ProxyContractObjBuilder, +> where + PairObjBuilder: 'static + Copy + Fn() -> pair::ContractObj, + FarmObjBuilder: 'static + Copy + Fn() -> farm_with_locked_rewards::ContractObj, + EnergyFactoryObjBuilder: 'static + Copy + Fn() -> energy_factory::ContractObj, + StakingContractObjBuilder: 'static + Copy + Fn() -> farm_staking::ContractObj, + ProxyContractObjBuilder: + 'static + Copy + Fn() -> farm_staking_proxy_legacy::ContractObj, +{ + pub owner_addr: Address, + pub user_addr: Address, + pub b_mock: BlockchainStateWrapper, + pub pair_wrapper: ContractObjWrapper, PairObjBuilder>, + pub lp_farm_wrapper: + ContractObjWrapper, FarmObjBuilder>, + pub energy_factory_wrapper: + ContractObjWrapper, EnergyFactoryObjBuilder>, + pub staking_farm_wrapper: + ContractObjWrapper, StakingContractObjBuilder>, + pub proxy_wrapper: ContractObjWrapper< + farm_staking_proxy_legacy::ContractObj, + ProxyContractObjBuilder, + >, +} + +impl< + PairObjBuilder, + FarmObjBuilder, + EnergyFactoryObjBuilder, + StakingContractObjBuilder, + ProxyContractObjBuilder, + > + FarmStakingSetup< + PairObjBuilder, + FarmObjBuilder, + EnergyFactoryObjBuilder, + StakingContractObjBuilder, + ProxyContractObjBuilder, + > +where + PairObjBuilder: 'static + Copy + Fn() -> pair::ContractObj, + FarmObjBuilder: 'static + Copy + Fn() -> farm_with_locked_rewards::ContractObj, + EnergyFactoryObjBuilder: 'static + Copy + Fn() -> energy_factory::ContractObj, + StakingContractObjBuilder: 'static + Copy + Fn() -> farm_staking::ContractObj, + ProxyContractObjBuilder: + 'static + Copy + Fn() -> farm_staking_proxy_legacy::ContractObj, +{ + pub fn new( + pair_builder: PairObjBuilder, + lp_farm_builder: FarmObjBuilder, + energy_factory_builder: EnergyFactoryObjBuilder, + staking_farm_builder: StakingContractObjBuilder, + proxy_builder: ProxyContractObjBuilder, + ) -> Self { + let rust_zero = rust_biguint!(0u64); + let mut b_mock = BlockchainStateWrapper::new(); + let owner_addr = b_mock.create_user_account(&rust_zero); + let user_addr = b_mock.create_user_account(&rust_biguint!(100_000_000)); + + let pair_wrapper = setup_pair(&owner_addr, &user_addr, &mut b_mock, pair_builder); + let energy_factory_wrapper = + setup_energy_factory(&owner_addr, &mut b_mock, energy_factory_builder); + let lp_farm_wrapper = setup_lp_farm( + &owner_addr, + energy_factory_wrapper.address_ref(), + &mut b_mock, + lp_farm_builder, + ); + + let staking_farm_wrapper = + setup_staking_farm(&owner_addr, &mut b_mock, staking_farm_builder); + let proxy_wrapper = setup_proxy( + &owner_addr, + lp_farm_wrapper.address_ref(), + staking_farm_wrapper.address_ref(), + pair_wrapper.address_ref(), + &mut b_mock, + proxy_builder, + ); + + add_proxy_to_whitelist( + &owner_addr, + proxy_wrapper.address_ref(), + &mut b_mock, + &lp_farm_wrapper, + &staking_farm_wrapper, + ); + + FarmStakingSetup { + owner_addr, + user_addr, + b_mock, + pair_wrapper, + lp_farm_wrapper, + energy_factory_wrapper, + staking_farm_wrapper, + proxy_wrapper, + } + } + + pub fn unstake_proxy( + &mut self, + dual_yield_token_nonce: u64, + dual_yield_token_amount: u64, + expected_wegld_amount: u64, + expected_lp_farm_rewards: u64, + expected_staking_rewards: u64, + expected_unbond_token_amount: u64, + expected_unbond_token_unlock_epoch: u64, + ) -> u64 { + let mut unbond_token_nonce = 0; + + self.b_mock + .execute_esdt_transfer( + &self.user_addr, + &self.proxy_wrapper, + DUAL_YIELD_TOKEN_ID, + dual_yield_token_nonce, + &rust_biguint!(dual_yield_token_amount), + |sc| { + let received_tokens = sc + .unstake_farm_tokens(managed_biguint!(1), managed_biguint!(1)) + .to_vec(); + let mut vec_index = 0; + + if expected_wegld_amount > 0 { + let wegld_payment = received_tokens.get(vec_index); + assert_eq!(wegld_payment.amount, expected_wegld_amount); + + vec_index += 1; + } + + if expected_lp_farm_rewards > 0 { + let lp_farm_rewards = received_tokens.get(vec_index); + assert_eq!(lp_farm_rewards.amount, expected_lp_farm_rewards); + + vec_index += 1; + } + + if expected_staking_rewards > 0 { + let staking_rewards = received_tokens.get(vec_index); + assert_eq!(staking_rewards.amount, expected_staking_rewards); + + vec_index += 1; + } + + let unbond_tokens = received_tokens.get(vec_index); + assert_eq!(unbond_tokens.amount, expected_unbond_token_amount); + + unbond_token_nonce = unbond_tokens.token_nonce; + }, + ) + .assert_ok(); + + self.b_mock.execute_in_managed_environment(|| { + let expected_attributes = UnbondSftAttributes { + unlock_epoch: expected_unbond_token_unlock_epoch, + }; + + self.b_mock.check_nft_balance( + &self.user_addr, + STAKING_FARM_TOKEN_ID, + unbond_token_nonce, + &rust_biguint!(expected_unbond_token_amount), + Some(&expected_attributes), + ); + }); + + unbond_token_nonce + } + + pub fn unbond_proxy( + &mut self, + unbond_token_nonce: u64, + unbond_token_amount: u64, + expected_token_out_amount: u64, + ) { + self.b_mock + .execute_esdt_transfer( + &self.user_addr, + &self.staking_farm_wrapper, + STAKING_FARM_TOKEN_ID, + unbond_token_nonce, + &rust_biguint!(unbond_token_amount), + |sc| { + let received_tokens = sc.unbond_farm(); + assert_eq!(received_tokens.amount, expected_token_out_amount); + }, + ) + .assert_ok(); + } +} diff --git a/legacy-contracts/farm-staking-proxy-legacy/tests/staking_farm_with_lp_staking_contract_setup/mod.rs b/legacy-contracts/farm-staking-proxy-legacy/tests/staking_farm_with_lp_staking_contract_setup/mod.rs new file mode 100644 index 000000000..0cca69853 --- /dev/null +++ b/legacy-contracts/farm-staking-proxy-legacy/tests/staking_farm_with_lp_staking_contract_setup/mod.rs @@ -0,0 +1,153 @@ +use external_contracts_interactions::ExternalContractsInteractionsModule; +use farm_token::FarmTokenModule; +use lp_farm_token::LpFarmTokenModule; +use multiversx_sc::{ + imports::StorageTokenWrapper, + types::{Address, EsdtLocalRole, MultiValueEncoded}, +}; +use multiversx_sc_scenario::{ + managed_address, managed_biguint, managed_token_id, rust_biguint, + testing_framework::{BlockchainStateWrapper, ContractObjWrapper}, + DebugApi, +}; + +use farm_staking::*; + +use farm_staking_proxy_legacy::dual_yield_token::DualYieldTokenModule; +use farm_staking_proxy_legacy::*; +use pausable::{PausableModule, State}; +use sc_whitelist_module::SCWhitelistModule; + +use crate::constants::*; + +pub fn setup_staking_farm( + owner_addr: &Address, + b_mock: &mut BlockchainStateWrapper, + builder: StakingContractObjBuilder, +) -> ContractObjWrapper, StakingContractObjBuilder> +where + StakingContractObjBuilder: 'static + Copy + Fn() -> farm_staking::ContractObj, +{ + let rust_zero = rust_biguint!(0u64); + let farm_staking_wrapper = + b_mock.create_sc_account(&rust_zero, Some(owner_addr), builder, PROXY_WASM_PATH); + + b_mock + .execute_tx(owner_addr, &farm_staking_wrapper, &rust_zero, |sc| { + let farming_token_id = managed_token_id!(STAKING_TOKEN_ID); + let div_const = managed_biguint!(DIVISION_SAFETY_CONSTANT); + let max_apr = managed_biguint!(MAX_APR); + + sc.init( + farming_token_id, + div_const, + max_apr, + UNBOND_EPOCHS, + managed_address!(owner_addr), + MultiValueEncoded::new(), + ); + + sc.farm_token() + .set_token_id(managed_token_id!(STAKING_FARM_TOKEN_ID)); + + sc.state().set(State::Active); + }) + .assert_ok(); + + b_mock.set_esdt_balance( + farm_staking_wrapper.address_ref(), + STAKING_REWARD_TOKEN_ID, + &rust_biguint!(REWARD_CAPACITY), + ); + + let farm_token_roles = [ + EsdtLocalRole::NftCreate, + EsdtLocalRole::NftAddQuantity, + EsdtLocalRole::NftBurn, + ]; + b_mock.set_esdt_local_roles( + farm_staking_wrapper.address_ref(), + STAKING_FARM_TOKEN_ID, + &farm_token_roles[..], + ); + + farm_staking_wrapper +} + +pub fn add_proxy_to_whitelist( + owner_addr: &Address, + proxy_address: &Address, + b_mock: &mut BlockchainStateWrapper, + lp_farm_builder: &ContractObjWrapper< + farm_with_locked_rewards::ContractObj, + FarmObjBuilder, + >, + staking_farm_builder: &ContractObjWrapper< + farm_staking::ContractObj, + StakingContractObjBuilder, + >, +) where + FarmObjBuilder: 'static + Copy + Fn() -> farm_with_locked_rewards::ContractObj, + StakingContractObjBuilder: 'static + Copy + Fn() -> farm_staking::ContractObj, +{ + let rust_zero = rust_biguint!(0u64); + b_mock + .execute_tx(owner_addr, lp_farm_builder, &rust_zero, |sc| { + sc.add_sc_address_to_whitelist(managed_address!(proxy_address)); + }) + .assert_ok(); + b_mock + .execute_tx(owner_addr, staking_farm_builder, &rust_zero, |sc| { + sc.add_sc_address_to_whitelist(managed_address!(proxy_address)); + }) + .assert_ok(); +} + +pub fn setup_proxy( + owner_addr: &Address, + lp_farm_address: &Address, + staking_farm_address: &Address, + pair_address: &Address, + b_mock: &mut BlockchainStateWrapper, + builder: ProxyContractObjBuilder, +) -> ContractObjWrapper, ProxyContractObjBuilder> +where + ProxyContractObjBuilder: + 'static + Copy + Fn() -> farm_staking_proxy_legacy::ContractObj, +{ + let rust_zero = rust_biguint!(0u64); + let proxy_wrapper = + b_mock.create_sc_account(&rust_zero, Some(owner_addr), builder, PROXY_WASM_PATH); + + b_mock + .execute_tx(&owner_addr, &proxy_wrapper, &rust_zero, |sc| { + sc.init(); + + sc.pair_address().set(managed_address!(pair_address)); + sc.lp_farm_address().set(managed_address!(lp_farm_address)); + sc.lp_farm_token_id() + .set(managed_token_id!(LP_FARM_TOKEN_ID)); + sc.staking_farm_address() + .set(managed_address!(staking_farm_address)); + sc.staking_token_id() + .set(managed_token_id!(STAKING_TOKEN_ID)); + sc.staking_farm_token_id() + .set(managed_token_id!(STAKING_FARM_TOKEN_ID)); + sc.dual_yield_token_id() + .set(&managed_token_id!(DUAL_YIELD_TOKEN_ID)); + }) + .assert_ok(); + + let dual_yield_token_roles = [ + EsdtLocalRole::NftCreate, + EsdtLocalRole::NftAddQuantity, + EsdtLocalRole::NftBurn, + ]; + b_mock.set_esdt_local_roles( + proxy_wrapper.address_ref(), + DUAL_YIELD_TOKEN_ID, + &dual_yield_token_roles[..], + ); + + proxy_wrapper +} diff --git a/legacy-contracts/farm-staking-proxy-legacy/wasm/Cargo.lock b/legacy-contracts/farm-staking-proxy-legacy/wasm/Cargo.lock index 1c15ac5fa..cb02bd384 100644 --- a/legacy-contracts/farm-staking-proxy-legacy/wasm/Cargo.lock +++ b/legacy-contracts/farm-staking-proxy-legacy/wasm/Cargo.lock @@ -20,6 +20,13 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +[[package]] +name = "common-types" +version = "0.0.0" +dependencies = [ + "multiversx-sc", +] + [[package]] name = "common_errors" version = "0.0.0" @@ -38,19 +45,163 @@ dependencies = [ "unwrappable", ] +[[package]] +name = "config" +version = "0.0.0" +dependencies = [ + "common_errors", + "common_structs", + "multiversx-sc", + "pausable", + "permissions_module", + "token_send", +] + +[[package]] +name = "contexts" +version = "0.0.0" +dependencies = [ + "common_errors", + "common_structs", + "config", + "farm_token", + "multiversx-sc", + "multiversx-sc-modules", + "pausable", + "permissions_module", + "rewards", + "token_merge_helper", + "token_send", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "endian-type" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" +[[package]] +name = "energy-factory" +version = "0.0.0" +dependencies = [ + "common_structs", + "legacy_token_decode_module", + "math", + "mergeable", + "multiversx-sc", + "multiversx-sc-modules", + "sc_whitelist_module", + "simple-lock", + "unwrappable", + "utils", +] + +[[package]] +name = "energy-query" +version = "0.0.0" +dependencies = [ + "energy-factory", + "multiversx-sc", +] + +[[package]] +name = "events" +version = "0.0.0" +dependencies = [ + "common_structs", + "contexts", + "multiversx-sc", +] + +[[package]] +name = "farm" +version = "0.0.0" +dependencies = [ + "common_errors", + "common_structs", + "config", + "contexts", + "energy-query", + "events", + "farm-boosted-yields", + "farm_base_impl", + "farm_token", + "fixed-supply-token", + "mergeable", + "multiversx-sc", + "multiversx-sc-modules", + "pair", + "pausable", + "permissions_module", + "rewards", + "sc_whitelist_module", + "token_send", + "utils", + "week-timekeeping", + "weekly-rewards-splitting", +] + +[[package]] +name = "farm-boosted-yields" +version = "0.0.0" +dependencies = [ + "common-types", + "config", + "energy-query", + "multiversx-sc", + "pausable", + "permissions_module", + "week-timekeeping", + "weekly-rewards-splitting", +] + +[[package]] +name = "farm-staking" +version = "0.0.0" +dependencies = [ + "common_errors", + "common_structs", + "config", + "contexts", + "energy-factory", + "energy-query", + "events", + "farm", + "farm-boosted-yields", + "farm_base_impl", + "farm_token", + "fixed-supply-token", + "math", + "mergeable", + "multiversx-sc", + "multiversx-sc-modules", + "pair", + "pausable", + "permissions_module", + "rewards", + "sc_whitelist_module", + "token_send", + "utils", + "week-timekeeping", + "weekly-rewards-splitting", +] + [[package]] name = "farm-staking-proxy-legacy" version = "0.0.0" dependencies = [ "common_structs", + "farm-staking", + "farm-with-locked-rewards", "multiversx-sc", "multiversx-sc-modules", + "pair", "token_merge_helper", ] @@ -62,6 +213,90 @@ dependencies = [ "multiversx-sc-wasm-adapter", ] +[[package]] +name = "farm-with-locked-rewards" +version = "0.0.0" +dependencies = [ + "common_errors", + "common_structs", + "config", + "contexts", + "energy-factory", + "energy-query", + "events", + "farm", + "farm-boosted-yields", + "farm_base_impl", + "farm_token", + "fixed-supply-token", + "locking_module", + "mergeable", + "multiversx-sc", + "multiversx-sc-modules", + "pausable", + "permissions_module", + "rewards", + "sc_whitelist_module", + "token_send", + "utils", + "week-timekeeping", + "weekly-rewards-splitting", +] + +[[package]] +name = "farm_base_impl" +version = "0.0.0" +dependencies = [ + "common_errors", + "common_structs", + "config", + "contexts", + "events", + "farm_token", + "fixed-supply-token", + "mergeable", + "multiversx-sc", + "multiversx-sc-modules", + "pausable", + "permissions_module", + "rewards", + "token_merge_helper", + "token_send", + "utils", +] + +[[package]] +name = "farm_token" +version = "0.0.0" +dependencies = [ + "common_errors", + "common_structs", + "config", + "multiversx-sc", + "multiversx-sc-modules", + "pausable", + "permissions_module", + "token_send", +] + +[[package]] +name = "fees-collector" +version = "0.0.0" +dependencies = [ + "common-types", + "common_errors", + "energy-factory", + "energy-query", + "locking_module", + "multiversx-sc", + "multiversx-sc-modules", + "sc_whitelist_module", + "simple-lock", + "utils", + "week-timekeeping", + "weekly-rewards-splitting", +] + [[package]] name = "fixed-supply-token" version = "0.0.0" @@ -81,6 +316,33 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "legacy_token_decode_module" +version = "0.0.0" +dependencies = [ + "common_structs", + "multiversx-sc", + "utils", +] + +[[package]] +name = "locking_module" +version = "0.0.0" +dependencies = [ + "energy-factory", + "multiversx-sc", + "simple-lock", +] + [[package]] name = "math" version = "0.0.0" @@ -181,6 +443,39 @@ dependencies = [ "autocfg", ] +[[package]] +name = "pair" +version = "0.0.0" +dependencies = [ + "common_errors", + "common_structs", + "fees-collector", + "itertools", + "multiversx-sc", + "pausable", + "permissions_module", + "simple-lock", + "token_send", + "utils", +] + +[[package]] +name = "pausable" +version = "0.0.0" +dependencies = [ + "multiversx-sc", + "permissions_module", +] + +[[package]] +name = "permissions_module" +version = "0.0.0" +dependencies = [ + "bitflags", + "common_errors", + "multiversx-sc", +] + [[package]] name = "proc-macro2" version = "1.0.82" @@ -209,6 +504,38 @@ dependencies = [ "nibble_vec", ] +[[package]] +name = "rewards" +version = "0.0.0" +dependencies = [ + "common_errors", + "common_structs", + "config", + "farm_token", + "multiversx-sc", + "multiversx-sc-modules", + "pausable", + "permissions_module", + "token_send", +] + +[[package]] +name = "sc_whitelist_module" +version = "0.0.0" +dependencies = [ + "common_errors", + "multiversx-sc", +] + +[[package]] +name = "simple-lock" +version = "0.0.0" +dependencies = [ + "common_structs", + "multiversx-sc", + "multiversx-sc-modules", +] + [[package]] name = "smallvec" version = "1.11.2" @@ -234,6 +561,15 @@ dependencies = [ "multiversx-sc", ] +[[package]] +name = "token_send" +version = "0.0.0" +dependencies = [ + "common_errors", + "common_structs", + "multiversx-sc", +] + [[package]] name = "unicode-ident" version = "1.0.12" @@ -252,3 +588,33 @@ version = "0.0.0" dependencies = [ "multiversx-sc", ] + +[[package]] +name = "utils" +version = "0.0.0" +dependencies = [ + "common_structs", + "fixed-supply-token", + "mergeable", + "multiversx-sc", +] + +[[package]] +name = "week-timekeeping" +version = "0.0.0" +dependencies = [ + "common-types", + "multiversx-sc", +] + +[[package]] +name = "weekly-rewards-splitting" +version = "0.0.0" +dependencies = [ + "common-types", + "energy-query", + "math", + "multiversx-sc", + "unwrappable", + "week-timekeeping", +]