diff --git a/Cargo.lock b/Cargo.lock index 4f8b88d23..068e52c2d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -589,6 +589,7 @@ dependencies = [ "num-bigint", "pair", "pausable", + "permissions-hub", "permissions_module", "rewards", "sc_whitelist_module", @@ -768,6 +769,7 @@ dependencies = [ "multiversx-sc-scenario", "num-bigint", "pausable", + "permissions-hub", "permissions_module", "rewards", "sc_whitelist_module", diff --git a/dex/farm-with-locked-rewards/Cargo.toml b/dex/farm-with-locked-rewards/Cargo.toml index eedd45925..f637b3249 100644 --- a/dex/farm-with-locked-rewards/Cargo.toml +++ b/dex/farm-with-locked-rewards/Cargo.toml @@ -74,6 +74,9 @@ path = "../../locked-asset/energy-factory" [dependencies.energy-query] path = "../../energy-integration/common-modules/energy-query" +[dependencies.permissions-hub] +path = "../permissions-hub" + [dependencies.multiversx-sc] version = "=0.53.2" features = ["esdt-token-payment-legacy-decode"] diff --git a/dex/farm-with-locked-rewards/src/external_interaction.rs b/dex/farm-with-locked-rewards/src/external_interaction.rs new file mode 100644 index 000000000..54ecf7510 --- /dev/null +++ b/dex/farm-with-locked-rewards/src/external_interaction.rs @@ -0,0 +1,171 @@ +multiversx_sc::imports!(); + +use common_structs::FarmTokenAttributes; +use farm::{ + base_functions::{self, ClaimRewardsResultType}, + exit_penalty, EnterFarmResultType, +}; + +use crate::NoMintWrapper; + +#[multiversx_sc::module] +pub trait ExternalInteractionsModule: + rewards::RewardsModule + + config::ConfigModule + + token_send::TokenSendModule + + farm_token::FarmTokenModule + + pausable::PausableModule + + permissions_module::PermissionsModule + + sc_whitelist_module::SCWhitelistModule + + events::EventsModule + + multiversx_sc_modules::default_issue_callbacks::DefaultIssueCallbacksModule + + base_functions::BaseFunctionsModule + + exit_penalty::ExitPenaltyModule + + locking_module::lock_with_energy_module::LockWithEnergyModule + + farm_base_impl::base_farm_init::BaseFarmInitModule + + farm_base_impl::base_farm_validation::BaseFarmValidationModule + + farm_base_impl::enter_farm::BaseEnterFarmModule + + farm_base_impl::claim_rewards::BaseClaimRewardsModule + + farm_base_impl::compound_rewards::BaseCompoundRewardsModule + + farm_base_impl::exit_farm::BaseExitFarmModule + + farm_boosted_yields::FarmBoostedYieldsModule + + farm_boosted_yields::boosted_yields_factors::BoostedYieldsFactorsModule + + week_timekeeping::WeekTimekeepingModule + + weekly_rewards_splitting::WeeklyRewardsSplittingModule + + weekly_rewards_splitting::events::WeeklyRewardsSplittingEventsModule + + weekly_rewards_splitting::global_info::WeeklyRewardsGlobalInfo + + weekly_rewards_splitting::locked_token_buckets::WeeklyRewardsLockedTokenBucketsModule + + weekly_rewards_splitting::update_claim_progress_energy::UpdateClaimProgressEnergyModule + + energy_query::EnergyQueryModule + + utils::UtilsModule +{ + #[payable("*")] + #[endpoint(enterFarmOnBehalf)] + fn enter_farm_on_behalf(&self, user: ManagedAddress) -> EnterFarmResultType { + let caller = self.blockchain().get_caller(); + self.require_user_whitelisted(&user, &caller); + + self.check_additional_payments_original_owner(&user); + + let boosted_rewards = self.claim_only_boosted_payment(&user); + let new_farm_token = self.enter_farm::>(user.clone()); + self.send_payment_non_zero(&caller, &new_farm_token); + + let locked_rewards_payment = if boosted_rewards == 0 { + let locked_token_id = self.get_locked_token_id(); + EsdtTokenPayment::new(locked_token_id, 0, boosted_rewards) + } else { + self.lock_virtual( + self.reward_token_id().get(), + boosted_rewards, + user.clone(), + user.clone(), + ) + }; + + self.update_energy_and_progress(&user); + + (new_farm_token, locked_rewards_payment).into() + } + + #[payable("*")] + #[endpoint(claimRewardsOnBehalf)] + fn claim_rewards_on_behalf(&self) -> ClaimRewardsResultType { + let user = self.check_and_return_original_owner(); + let caller = self.blockchain().get_caller(); + self.require_user_whitelisted(&user, &caller); + + let claim_rewards_result = self.claim_rewards::>(user.clone()); + + self.send_payment_non_zero(&caller, &claim_rewards_result.new_farm_token); + + let rewards_payment = claim_rewards_result.rewards; + let locked_rewards_payment = if rewards_payment.amount == 0 { + let locked_token_id = self.get_locked_token_id(); + EsdtTokenPayment::new(locked_token_id, 0, rewards_payment.amount) + } else { + self.lock_virtual( + rewards_payment.token_identifier, + rewards_payment.amount, + user.clone(), + user, + ) + }; + + (claim_rewards_result.new_farm_token, locked_rewards_payment).into() + } + + fn check_and_return_original_owner(&self) -> ManagedAddress { + let payments = self.call_value().all_esdt_transfers().clone_value(); + let farm_token_mapper = self.farm_token(); + let mut original_owner = ManagedAddress::zero(); + for payment in payments.into_iter() { + let attributes: FarmTokenAttributes = + farm_token_mapper.get_token_attributes(payment.token_nonce); + + if original_owner.is_zero() { + original_owner = attributes.original_owner; + } else { + require!( + original_owner == attributes.original_owner, + "All position must have the same original owner" + ); + } + } + + require!( + !original_owner.is_zero(), + "Original owner could not be identified" + ); + + original_owner + } + + fn check_additional_payments_original_owner(&self, user: &ManagedAddress) { + let payments = self.call_value().all_esdt_transfers().clone_value(); + if payments.len() == 1 { + return; + } + + let farm_token_mapper = self.farm_token(); + let farm_token_id = farm_token_mapper.get_token_id(); + for payment in payments.into_iter() { + if payment.token_identifier != farm_token_id { + continue; + } + + let attributes: FarmTokenAttributes = + farm_token_mapper.get_token_attributes(payment.token_nonce); + + require!( + user == &attributes.original_owner, + "Provided address is not the same as the original owner" + ); + } + } + + fn require_user_whitelisted(&self, user: &ManagedAddress, authorized_address: &ManagedAddress) { + let permissions_hub_address = self.permissions_hub_address().get(); + let is_whitelisted: bool = self + .permissions_hub_proxy(permissions_hub_address) + .is_whitelisted(user, authorized_address) + .execute_on_dest_context(); + + require!(is_whitelisted, "Caller is not whitelisted by the user"); + } + + #[only_owner] + #[endpoint(setPermissionsHubAddress)] + fn set_permissions_hub_address(&self, address: ManagedAddress) { + self.permissions_hub_address().set(&address); + } + + #[proxy] + fn permissions_hub_proxy( + &self, + sc_address: ManagedAddress, + ) -> permissions_hub::Proxy; + + #[storage_mapper("permissionsHubAddress")] + fn permissions_hub_address(&self) -> SingleValueMapper; +} diff --git a/dex/farm-with-locked-rewards/src/lib.rs b/dex/farm-with-locked-rewards/src/lib.rs index 7434704d1..fefbf6275 100644 --- a/dex/farm-with-locked-rewards/src/lib.rs +++ b/dex/farm-with-locked-rewards/src/lib.rs @@ -3,6 +3,8 @@ multiversx_sc::imports!(); multiversx_sc::derive_imports!(); +pub mod external_interaction; + use common_structs::FarmTokenAttributes; use contexts::storage_cache::StorageCache; use core::marker::PhantomData; @@ -32,6 +34,7 @@ pub trait Farm: + multiversx_sc_modules::default_issue_callbacks::DefaultIssueCallbacksModule + farm::base_functions::BaseFunctionsModule + farm::exit_penalty::ExitPenaltyModule + + external_interaction::ExternalInteractionsModule + farm_base_impl::base_farm_init::BaseFarmInitModule + farm_base_impl::base_farm_validation::BaseFarmValidationModule + farm_base_impl::enter_farm::BaseEnterFarmModule diff --git a/dex/farm-with-locked-rewards/wasm/Cargo.lock b/dex/farm-with-locked-rewards/wasm/Cargo.lock index 897ce8c8e..aa7396f98 100644 --- a/dex/farm-with-locked-rewards/wasm/Cargo.lock +++ b/dex/farm-with-locked-rewards/wasm/Cargo.lock @@ -138,6 +138,7 @@ dependencies = [ "multiversx-sc-modules", "pair", "pausable", + "permissions-hub", "permissions_module", "rewards", "sc_whitelist_module", @@ -182,6 +183,7 @@ dependencies = [ "multiversx-sc", "multiversx-sc-modules", "pausable", + "permissions-hub", "permissions_module", "rewards", "sc_whitelist_module", @@ -423,6 +425,13 @@ dependencies = [ "permissions_module", ] +[[package]] +name = "permissions-hub" +version = "0.0.0" +dependencies = [ + "multiversx-sc", +] + [[package]] name = "permissions_module" version = "0.0.0" diff --git a/dex/farm-with-locked-rewards/wasm/src/lib.rs b/dex/farm-with-locked-rewards/wasm/src/lib.rs index e8aa3099f..6efe13330 100644 --- a/dex/farm-with-locked-rewards/wasm/src/lib.rs +++ b/dex/farm-with-locked-rewards/wasm/src/lib.rs @@ -6,9 +6,9 @@ // Init: 1 // Upgrade: 1 -// Endpoints: 66 +// Endpoints: 69 // Async Callback: 1 -// Total number of exported functions: 69 +// Total number of exported functions: 72 #![no_std] @@ -66,6 +66,9 @@ multiversx_sc_wasm_adapter::endpoints! { getMinimumFarmingEpoch => minimum_farming_epochs getBurnGasLimit => burn_gas_limit getPairContractManagedAddress => pair_contract_address + enterFarmOnBehalf => enter_farm_on_behalf + claimRewardsOnBehalf => claim_rewards_on_behalf + setPermissionsHubAddress => set_permissions_hub_address collectUndistributedBoostedRewards => collect_undistributed_boosted_rewards getBoostedYieldsRewardsPercentage => boosted_yields_rewards_percentage getAccumulatedRewardsForWeek => accumulated_rewards_for_week