diff --git a/Cargo.lock b/Cargo.lock index 32820097b..5b82e337a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -791,6 +791,50 @@ dependencies = [ "multiversx-sc-meta-lib", ] +[[package]] +name = "farm-with-top-up" +version = "0.0.0" +dependencies = [ + "common_errors", + "common_structs", + "config", + "contexts", + "energy-factory-mock", + "energy-query", + "energy-update", + "events", + "farm-boosted-yields", + "farm_base_impl", + "farm_token", + "fixed-supply-token", + "mergeable", + "multiversx-sc", + "multiversx-sc-modules", + "multiversx-sc-scenario", + "num-bigint", + "original_owner_helper", + "pair", + "pausable", + "permissions-hub", + "permissions_hub_module", + "permissions_module", + "rewards", + "sc_whitelist_module", + "simple-lock", + "timestamp-oracle", + "utils", + "week-timekeeping", + "weekly-rewards-splitting", +] + +[[package]] +name = "farm-with-top-up-abi" +version = "0.0.0" +dependencies = [ + "farm-with-top-up", + "multiversx-sc-meta-lib", +] + [[package]] name = "farm_base_impl" version = "0.0.0" diff --git a/Cargo.toml b/Cargo.toml index fa0bdca20..a5f953023 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,8 @@ members = [ "dex", "dex/farm", "dex/farm/meta", + "dex/farm-with-top-up", + "dex/farm-with-top-up/meta", "dex/farm-with-locked-rewards", "dex/farm-with-locked-rewards/meta", "dex/pair", diff --git a/common/modules/farm/farm_base_impl/src/base_traits_impl.rs b/common/modules/farm/farm_base_impl/src/base_traits_impl.rs index f5cc83be3..1ed0766ad 100644 --- a/common/modules/farm/farm_base_impl/src/base_traits_impl.rs +++ b/common/modules/farm/farm_base_impl/src/base_traits_impl.rs @@ -73,19 +73,18 @@ pub trait FarmContract { ) -> BigUint<::Api> { let current_block_nonce = sc.blockchain().get_block_nonce(); let last_reward_nonce = sc.last_reward_block_nonce().get(); - if current_block_nonce > last_reward_nonce { - let to_mint = - Self::calculate_per_block_rewards(sc, current_block_nonce, last_reward_nonce); - if to_mint != 0 { - Self::mint_rewards(sc, token_id, &to_mint); - } - - sc.last_reward_block_nonce().set(current_block_nonce); - - to_mint - } else { - BigUint::zero() + if current_block_nonce <= last_reward_nonce { + return BigUint::zero(); + } + + let to_mint = Self::calculate_per_block_rewards(sc, current_block_nonce, last_reward_nonce); + if to_mint != 0 { + Self::mint_rewards(sc, token_id, &to_mint); } + + sc.last_reward_block_nonce().set(current_block_nonce); + + to_mint } fn generate_aggregated_rewards( @@ -93,15 +92,19 @@ pub trait FarmContract { storage_cache: &mut StorageCache, ) { let total_reward = Self::mint_per_block_rewards(sc, &storage_cache.reward_token_id); - if total_reward > 0u64 { - storage_cache.reward_reserve += &total_reward; - - if storage_cache.farm_token_supply != 0u64 { - let increase = (&total_reward * &storage_cache.division_safety_constant) - / &storage_cache.farm_token_supply; - storage_cache.reward_per_share += &increase; - } + if total_reward == 0 { + return; } + + storage_cache.reward_reserve += &total_reward; + + if storage_cache.farm_token_supply == 0 { + return; + } + + let increase = (&total_reward * &storage_cache.division_safety_constant) + / &storage_cache.farm_token_supply; + storage_cache.reward_per_share += &increase; } fn calculate_rewards( @@ -112,12 +115,12 @@ pub trait FarmContract { storage_cache: &StorageCache, ) -> BigUint<::Api> { let token_rps = token_attributes.get_reward_per_share(); - if storage_cache.reward_per_share > token_rps { - let rps_diff = &storage_cache.reward_per_share - &token_rps; - farm_token_amount * &rps_diff / &storage_cache.division_safety_constant - } else { - BigUint::zero() + if storage_cache.reward_per_share <= token_rps { + return BigUint::zero(); } + + let rps_diff = &storage_cache.reward_per_share - &token_rps; + farm_token_amount * &rps_diff / &storage_cache.division_safety_constant } fn create_enter_farm_initial_attributes( diff --git a/dex/farm-with-top-up/Cargo.toml b/dex/farm-with-top-up/Cargo.toml new file mode 100644 index 000000000..3fc1529f8 --- /dev/null +++ b/dex/farm-with-top-up/Cargo.toml @@ -0,0 +1,100 @@ +[package] +name = "farm-with-top-up" +version = "0.0.0" +authors = ["MultiversX "] +edition = "2021" +publish = false + +[lib] +path = "src/lib.rs" + +[dependencies.farm_base_impl] +path = "../../common/modules/farm/farm_base_impl" + +[dependencies.config] +path = "../../common/modules/farm/config" + +[dependencies.farm_token] +path = "../../common/modules/farm/farm_token" + +[dependencies.rewards] +path = "../../common/modules/farm/rewards" + +[dependencies.events] +path = "../../common/modules/farm/events" + +[dependencies.contexts] +path = "../../common/modules/farm/contexts" + +[dependencies.utils] +path = "../../common/modules/utils" + +[dependencies.pausable] +path = "../../common/modules/pausable" + +[dependencies.permissions_module] +path = "../../common/modules/permissions_module" + +[dependencies.permissions_hub_module] +path = "../../common/modules/permissions_hub_module" + +[dependencies.original_owner_helper] +path = "../../common/modules/original_owner_helper" + +[dependencies.sc_whitelist_module] +path = "../../common/modules/sc_whitelist_module" + +[dependencies.pair] +path = "../pair" + +[dependencies.common_structs] +path = "../../common/common_structs" + +[dependencies.common_errors] +path = "../../common/common_errors" + +[dependencies.mergeable] +path = "../../common/traits/mergeable" + +[dependencies.fixed-supply-token] +path = "../../common/traits/fixed-supply-token" + +[dependencies.farm-boosted-yields] +path = "../../energy-integration/farm-boosted-yields" + +[dependencies.week-timekeeping] +path = "../../energy-integration/common-modules/week-timekeeping" + +[dependencies.weekly-rewards-splitting] +path = "../../energy-integration/common-modules/weekly-rewards-splitting" + +[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"] + +[dependencies.multiversx-sc-modules] +version = "=0.53.2" + +[dev-dependencies] +num-bigint = "0.4.2" + +[dev-dependencies.energy-update] +path = "../../energy-integration/energy-update" + +[dev-dependencies.multiversx-sc-scenario] +version = "=0.53.2" + +[dev-dependencies.energy-factory-mock] +path = "../../energy-integration/energy-factory-mock" + +[dev-dependencies.simple-lock] +path = "../../locked-asset/simple-lock" + +[dev-dependencies.timestamp-oracle] +path = "../../energy-integration/timestamp-oracle" diff --git a/dex/farm-with-top-up/README.md b/dex/farm-with-top-up/README.md new file mode 100644 index 000000000..fcc860299 --- /dev/null +++ b/dex/farm-with-top-up/README.md @@ -0,0 +1,105 @@ +# Farm Smart Contract + +## Abstract + +Liquidity providers of xExchange are incentivized with MEX rewards in exchange for them locking their LP tokens in Farm contracts. + +## Introduction + +This smart contract has the role of generating and distributing MEX tokens to liquidity providers that choose to lock their LP tokens, thus increasing the ecosystem stability. + +## Endpoints + +### init + +```rust + #[init] + fn init( + &self, + reward_token_id: TokenIdentifier, + farming_token_id: TokenIdentifier, + division_safety_constant: BigUint, + pair_contract_address: ManagedAddress, + ); +``` + +The arguments are: + +- __reward_token_id__ - MEX token ID +- __farming_token_id__ - token used for farming - LP tokens usually +- __division_safety_constant__ - a constant that is used for math safety functions - increasing precision of reward distribution +- __pair_contract_address__ - almost each farm contract has an associated pair contract, exception being the MEX farm. This address needs to be known because in case of penalty burn, the farm will need the Pair contract in order to convert LP tokens to MEX and then burn them + +### enterFarm + +```rust + #[payable("*")] + #[endpoint(enterFarm)] + fn enter_farm(&self); +``` + +This endpoint receives at least one payment: + +- The first payment has to be of type __farming_token_id__. The actual token that is meant to be locked inside the Farm contract. +- The additional payments, if any, will be Farm positions and will be used to be merged with the newly created position, in order to consolidate all previous positions with the current one. + +This endpoint will give back to the caller a Farm position as a result. The Farm position is a META esdt that contains, in its attributes, information about the user input tokens and the current state of the contract when the user did enter. This information will be later used when trying to claim rewards or exit farm. + +### exitFarm + +```rust + #[payable("*")] + #[endpoint(exitFarm)] + fn exit_farm(&self); +``` + +This endpoint receives one payment, and that is the Farm Position. Based on an internal counter that the contract keeps track of, which is the __rps__ - meaning reward per share, the contract can calculate the reward that it needs to return to the caller for those specific tokens that he has sent. The output will consist of two payments: the LP tokens initially added and the accumulated rewards. + +This contract simulates per-block-reward-generation by keeping track of the last block that generated mex and keeps updating on every endpoint execution. Everytime an execution happens, the contract will generate the rewards for previous blocks. This is the case for the first successful TX inside a block, so only once per block this check has to be made and the action to be taken. + +If a user decides to exit too early, they will receive a penalty. This contract will take a part of its input LP tokens and will used them to buyback-and-burn MEX. This is done via a smart contract call to the configured pair contract address, via __removeLiquidityAndBuybackAndBurnToken__ endpoint. + +### claimRewards + +```rust + #[payable("*")] + #[endpoint(claimRewards)] + fn claim_rewards(&self); +``` + +This endpoint receives at least one payment: + +- The first payment is a Farm position that is 'harvested'. So for this position, the contract will calculate the reward and will return it to its caller. The contract will create a new position that has the ```RPS`` (Reward per share) reset. +- The additional payments, if any, will be other Farm positions and will be used to be merged with the newly created one. + +### compoundRewards + +```rust + #[payable("*")] + #[endpoint(compoundRewards)] + fn compound_rewards(&self); +``` + +This endpoint is similar with claimRewards, the differences being that instead of giving back the rewards to the caller, they are compounded into the newly created position (with the reset RPS). For this to be possible, reward token and farming token have to be the same, hence it is applicable only in case of MEX Farm. + +### mergePositions + +```rust + #[payable("*")] + #[endpoint(mergeFarmTokens)] + fn merge_farm_tokens(&self); +``` + +This endpoint merges two or more farm positions together and returns a single consolidated position to the caller. + +## Testing + +Aside from the scenario tests, there are a lot of tests that are available in the rust test suite. + +## Interaction + +The interaction scripts for this contract are located in the dex subdirectory of the root project directory. + +## Deployment + +The deployment of this contract is done using interaction scripts and it is managed by its admin (regular wallet at the moment, yet soon to be governance smart contract). diff --git a/dex/farm-with-top-up/meta/Cargo.toml b/dex/farm-with-top-up/meta/Cargo.toml new file mode 100644 index 000000000..d49165228 --- /dev/null +++ b/dex/farm-with-top-up/meta/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "farm-with-top-up-abi" +version = "0.0.0" +authors = ["MultiversX "] +edition = "2021" +publish = false + +[dependencies.farm-with-top-up] +path = ".." + +[dependencies.multiversx-sc-meta-lib] +version = "0.53.2" +default-features = false diff --git a/dex/farm-with-top-up/meta/src/main.rs b/dex/farm-with-top-up/meta/src/main.rs new file mode 100644 index 000000000..5d5b030bc --- /dev/null +++ b/dex/farm-with-top-up/meta/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + multiversx_sc_meta_lib::cli_main::(); +} diff --git a/dex/farm-with-top-up/multiversx.json b/dex/farm-with-top-up/multiversx.json new file mode 100644 index 000000000..736553962 --- /dev/null +++ b/dex/farm-with-top-up/multiversx.json @@ -0,0 +1,3 @@ +{ + "language": "rust" +} \ No newline at end of file diff --git a/dex/farm-with-top-up/src/base_functions.rs b/dex/farm-with-top-up/src/base_functions.rs new file mode 100644 index 000000000..7402eafb6 --- /dev/null +++ b/dex/farm-with-top-up/src/base_functions.rs @@ -0,0 +1,324 @@ +#![allow(clippy::too_many_arguments)] +#![allow(clippy::from_over_into)] + +multiversx_sc::imports!(); +multiversx_sc::derive_imports!(); + +use core::marker::PhantomData; + +use common_errors::ERROR_ZERO_AMOUNT; +use common_structs::FarmTokenAttributes; +use contexts::storage_cache::StorageCache; + +use farm_base_impl::base_traits_impl::{DefaultFarmWrapper, FarmContract}; + +pub type DoubleMultiPayment = MultiValue2, EsdtTokenPayment>; +pub type ClaimRewardsResultType = DoubleMultiPayment; +pub type ExitFarmResultType = DoubleMultiPayment; + +pub const DEFAULT_FARM_POSITION_MIGRATION_NONCE: u64 = 1; + +pub struct ClaimRewardsResultWrapper { + pub new_farm_token: EsdtTokenPayment, + pub rewards: EsdtTokenPayment, +} + +pub struct ExitFarmResultWrapper { + pub farming_tokens: EsdtTokenPayment, + pub rewards: EsdtTokenPayment, +} + +impl Into> for ClaimRewardsResultWrapper { + fn into(self) -> ClaimRewardsResultType { + (self.new_farm_token, self.rewards).into() + } +} + +impl Into> for ExitFarmResultWrapper { + fn into(self) -> ExitFarmResultType { + (self.farming_tokens, self.rewards).into() + } +} + +#[multiversx_sc::module] +pub trait BaseFunctionsModule: + rewards::RewardsModule + + config::ConfigModule + + farm_token::FarmTokenModule + + pausable::PausableModule + + permissions_module::PermissionsModule + + events::EventsModule + + multiversx_sc_modules::default_issue_callbacks::DefaultIssueCallbacksModule + + 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 + + utils::UtilsModule + + farm_boosted_yields::FarmBoostedYieldsModule + + farm_boosted_yields::boosted_yields_factors::BoostedYieldsFactorsModule + + farm_boosted_yields::custom_reward_logic::CustomRewardLogicModule + + 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 +{ + fn enter_farm>( + &self, + caller: ManagedAddress, + ) -> EsdtTokenPayment { + let payments = self.call_value().all_esdt_transfers().clone_value(); + let base_enter_farm_result = self.enter_farm_base::(caller.clone(), payments); + + self.set_farm_supply_for_current_week( + &base_enter_farm_result.storage_cache.farm_token_supply, + ); + + self.emit_enter_farm_event( + &caller, + base_enter_farm_result.context.farming_token_payment, + base_enter_farm_result.new_farm_token.clone(), + base_enter_farm_result.created_with_merge, + base_enter_farm_result.storage_cache, + ); + + base_enter_farm_result.new_farm_token.payment + } + + fn claim_rewards>( + &self, + caller: ManagedAddress, + ) -> ClaimRewardsResultWrapper { + let payments = self.call_value().all_esdt_transfers().clone_value(); + let base_claim_rewards_result = self.claim_rewards_base::(caller.clone(), payments); + + let output_farm_token_payment = base_claim_rewards_result.new_farm_token.payment.clone(); + let rewards_payment = base_claim_rewards_result.rewards; + + self.set_farm_supply_for_current_week( + &base_claim_rewards_result.storage_cache.farm_token_supply, + ); + + self.emit_claim_rewards_event( + &caller, + base_claim_rewards_result.context, + base_claim_rewards_result.new_farm_token, + rewards_payment.clone(), + base_claim_rewards_result.created_with_merge, + base_claim_rewards_result.storage_cache, + ); + + ClaimRewardsResultWrapper { + new_farm_token: output_farm_token_payment, + rewards: rewards_payment, + } + } + + fn compound_rewards>( + &self, + caller: ManagedAddress, + ) -> EsdtTokenPayment { + let payments = self.call_value().all_esdt_transfers().clone_value(); + let base_compound_rewards_result = + self.compound_rewards_base::(caller.clone(), payments); + + let output_farm_token_payment = base_compound_rewards_result.new_farm_token.payment.clone(); + + self.set_farm_supply_for_current_week( + &base_compound_rewards_result.storage_cache.farm_token_supply, + ); + + self.emit_compound_rewards_event( + &caller, + base_compound_rewards_result.context, + base_compound_rewards_result.new_farm_token, + base_compound_rewards_result.compounded_rewards, + base_compound_rewards_result.created_with_merge, + base_compound_rewards_result.storage_cache, + ); + + output_farm_token_payment + } + + fn exit_farm>( + &self, + caller: ManagedAddress, + payment: EsdtTokenPayment, + ) -> ExitFarmResultWrapper { + let base_exit_farm_result = self.exit_farm_base::(caller.clone(), payment); + + let farming_token_payment = base_exit_farm_result.farming_token_payment; + let reward_payment = base_exit_farm_result.reward_payment; + + self.set_farm_supply_for_current_week( + &base_exit_farm_result.storage_cache.farm_token_supply, + ); + + self.emit_exit_farm_event( + &caller, + base_exit_farm_result.context, + farming_token_payment.clone(), + reward_payment.clone(), + base_exit_farm_result.storage_cache, + ); + + ExitFarmResultWrapper { + farming_tokens: farming_token_payment, + rewards: reward_payment, + } + } + + fn merge_and_return_attributes>( + &self, + orig_caller: &ManagedAddress, + ) -> FC::AttributesType { + let payments = self.get_non_empty_payments(); + let token_mapper = self.farm_token(); + token_mapper.require_all_same_token(&payments); + + self.check_and_update_user_farm_position::( + orig_caller, + &payments, + &self.farm_token(), + ); + + self.merge_from_payments_and_burn(payments, &token_mapper) + } + + fn claim_only_boosted_payment(&self, caller: &ManagedAddress) -> BigUint { + let reward = Wrapper::::calculate_boosted_rewards(self, caller); + if reward > 0 { + self.reward_reserve().update(|reserve| *reserve -= &reward); + } + + reward + } + + fn migrate_old_farm_positions(&self, caller: &ManagedAddress) -> BigUint { + let payments = self.get_non_empty_payments(); + let farm_token_mapper = self.farm_token(); + let farm_token_id = farm_token_mapper.get_token_id(); + let mut migrated_amount = BigUint::zero(); + for farm_position in &payments { + if farm_position.token_identifier == farm_token_id + && self.is_old_farm_position(farm_position.token_nonce) + { + migrated_amount += farm_position.amount; + } + } + + if migrated_amount > 0 { + self.user_total_farm_position(caller) + .update(|total_farm_position| *total_farm_position += &migrated_amount); + } + + migrated_amount + } + + fn decrease_old_farm_positions(&self, migrated_amount: BigUint, caller: &ManagedAddress) { + if migrated_amount == BigUint::zero() { + return; + } + + let user_total_farm_position_mapper = self.user_total_farm_position(caller); + let mut user_total_farm_position = user_total_farm_position_mapper.get(); + + if user_total_farm_position > migrated_amount { + user_total_farm_position -= &migrated_amount; + user_total_farm_position_mapper.set(user_total_farm_position); + } else { + user_total_farm_position_mapper.clear(); + } + } + + fn end_produce_rewards>(&self) { + let mut storage = StorageCache::new(self); + FC::generate_aggregated_rewards(self, &mut storage); + + self.produce_rewards_enabled().set(false); + } + + fn set_per_block_rewards>(&self, per_block_amount: BigUint) { + require!(per_block_amount != 0u64, ERROR_ZERO_AMOUNT); + + let mut storage = StorageCache::new(self); + FC::generate_aggregated_rewards(self, &mut storage); + + self.per_block_reward_amount().set(&per_block_amount); + } + + fn require_queried(&self) { + let caller = self.blockchain().get_caller(); + let sc_address = self.blockchain().get_sc_address(); + require!( + caller == sc_address, + "May only call this function through VM query" + ); + } +} + +pub struct Wrapper { + _phantom: PhantomData, +} + +impl Wrapper +where + T: BaseFunctionsModule + farm_boosted_yields::FarmBoostedYieldsModule, +{ + pub fn calculate_boosted_rewards( + sc: &::FarmSc, + caller: &ManagedAddress<<::FarmSc as ContractBase>::Api>, + ) -> BigUint<<::FarmSc as ContractBase>::Api> { + let user_total_farm_position = sc.user_total_farm_position(caller).get(); + + sc.claim_boosted_yields_rewards(caller, user_total_farm_position) + } +} + +impl FarmContract for Wrapper +where + T: BaseFunctionsModule + farm_boosted_yields::FarmBoostedYieldsModule, +{ + type FarmSc = T; + type AttributesType = FarmTokenAttributes<::Api>; + + fn generate_aggregated_rewards( + sc: &Self::FarmSc, + storage_cache: &mut StorageCache, + ) { + let total_reward = Self::mint_per_block_rewards(sc, &storage_cache.reward_token_id); + if total_reward > 0u64 { + storage_cache.reward_reserve += &total_reward; + let split_rewards = sc.take_reward_slice(total_reward); + + if storage_cache.farm_token_supply != 0u64 { + let increase = (&split_rewards.base_farm * &storage_cache.division_safety_constant) + / &storage_cache.farm_token_supply; + storage_cache.reward_per_share += &increase; + } + } + } + + fn calculate_rewards( + sc: &Self::FarmSc, + caller: &ManagedAddress<::Api>, + farm_token_amount: &BigUint<::Api>, + token_attributes: &Self::AttributesType, + storage_cache: &StorageCache, + ) -> BigUint<::Api> { + let base_farm_reward = DefaultFarmWrapper::::calculate_rewards( + sc, + caller, + farm_token_amount, + token_attributes, + storage_cache, + ); + let boosted_yield_rewards = Self::calculate_boosted_rewards(sc, caller); + + base_farm_reward + boosted_yield_rewards + } +} diff --git a/dex/farm-with-top-up/src/external_interaction.rs b/dex/farm-with-top-up/src/external_interaction.rs new file mode 100644 index 000000000..db3275e36 --- /dev/null +++ b/dex/farm-with-top-up/src/external_interaction.rs @@ -0,0 +1,93 @@ +multiversx_sc::imports!(); + +use common_structs::FarmTokenAttributes; + +use crate::{ + base_functions::{self, ClaimRewardsResultType, Wrapper}, + EnterFarmResultType, +}; + +#[multiversx_sc::module] +pub trait ExternalInteractionsModule: + rewards::RewardsModule + + config::ConfigModule + + farm_token::FarmTokenModule + + pausable::PausableModule + + permissions_module::PermissionsModule + + permissions_hub_module::PermissionsHubModule + + original_owner_helper::OriginalOwnerHelperModule + + sc_whitelist_module::SCWhitelistModule + + events::EventsModule + + multiversx_sc_modules::default_issue_callbacks::DefaultIssueCallbacksModule + + base_functions::BaseFunctionsModule + + 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 + + farm_boosted_yields::custom_reward_logic::CustomRewardLogicModule + + 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); + + let payments = self.get_non_empty_payments(); + let farm_token_mapper = self.farm_token(); + self.check_additional_payments_original_owner::>( + &user, + &payments, + &farm_token_mapper, + ); + + let boosted_rewards = self.claim_only_boosted_payment(&user); + + let boosted_rewards_payment = + EsdtTokenPayment::new(self.reward_token_id().get(), 0, boosted_rewards); + + let new_farm_token = self.enter_farm::>(user.clone()); + self.send() + .direct_non_zero_esdt_payment(&caller, &new_farm_token); + self.send() + .direct_non_zero_esdt_payment(&user, &boosted_rewards_payment); + + self.update_energy_and_progress(&user); + + (new_farm_token, boosted_rewards_payment).into() + } + + #[payable("*")] + #[endpoint(claimRewardsOnBehalf)] + fn claim_rewards_on_behalf(&self) -> ClaimRewardsResultType { + let payments = self.get_non_empty_payments(); + let farm_token_mapper = self.farm_token(); + + let caller = self.blockchain().get_caller(); + let user = self.check_and_return_original_owner::>( + &payments, + &farm_token_mapper, + ); + self.require_user_whitelisted(&user, &caller); + + let claim_rewards_result = self.claim_rewards::>(user.clone()); + + self.send() + .direct_non_zero_esdt_payment(&caller, &claim_rewards_result.new_farm_token); + self.send() + .direct_non_zero_esdt_payment(&user, &claim_rewards_result.rewards); + + claim_rewards_result.into() + } +} diff --git a/dex/farm-with-top-up/src/lib.rs b/dex/farm-with-top-up/src/lib.rs new file mode 100644 index 000000000..10c0179e7 --- /dev/null +++ b/dex/farm-with-top-up/src/lib.rs @@ -0,0 +1,317 @@ +#![no_std] + +multiversx_sc::imports!(); +multiversx_sc::derive_imports!(); + +pub mod base_functions; +pub mod external_interaction; + +use base_functions::{ClaimRewardsResultType, DoubleMultiPayment, Wrapper}; +use common_structs::FarmTokenAttributes; +use contexts::storage_cache::StorageCache; + +use farm_base_impl::base_traits_impl::FarmContract; +use fixed_supply_token::FixedSupplyToken; + +pub type EnterFarmResultType = DoubleMultiPayment; +pub type ExitFarmWithPartialPosResultType = DoubleMultiPayment; + +pub const MAX_PERCENT: u64 = 10_000; + +#[multiversx_sc::contract] +pub trait Farm: + rewards::RewardsModule + + config::ConfigModule + + farm_token::FarmTokenModule + + pausable::PausableModule + + permissions_module::PermissionsModule + + permissions_hub_module::PermissionsHubModule + + original_owner_helper::OriginalOwnerHelperModule + + sc_whitelist_module::SCWhitelistModule + + events::EventsModule + + multiversx_sc_modules::default_issue_callbacks::DefaultIssueCallbacksModule + + base_functions::BaseFunctionsModule + + external_interaction::ExternalInteractionsModule + + 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 + + farm_boosted_yields::custom_reward_logic::CustomRewardLogicModule + + 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 +{ + #[init] + fn init( + &self, + reward_token_id: TokenIdentifier, + farming_token_id: TokenIdentifier, + division_safety_constant: BigUint, + owner: ManagedAddress, + timestamp_oracle_address: ManagedAddress, + admins: MultiValueEncoded, + ) { + self.base_farm_init( + reward_token_id, + farming_token_id, + division_safety_constant, + owner, + admins, + ); + + self.set_timestamp_oracle_address(timestamp_oracle_address); + + let current_epoch = self.blockchain().get_block_epoch(); + self.first_week_start_epoch().set(current_epoch); + } + + #[upgrade] + fn upgrade(&self, timestamp_oracle_address: ManagedAddress) { + if self.first_week_start_epoch().is_empty() { + let current_epoch = self.blockchain().get_block_epoch(); + self.first_week_start_epoch().set(current_epoch); + } + + // Farm position migration code + let farm_token_mapper = self.farm_token(); + self.try_set_farm_position_migration_nonce(farm_token_mapper); + + self.set_timestamp_oracle_address(timestamp_oracle_address); + } + + #[payable("*")] + #[endpoint(enterFarm)] + fn enter_farm_endpoint( + &self, + opt_orig_caller: OptionalValue, + ) -> EnterFarmResultType { + let caller = self.blockchain().get_caller(); + let orig_caller = self.get_orig_caller_from_opt(&caller, opt_orig_caller); + + self.migrate_old_farm_positions(&orig_caller); + + let boosted_rewards = self.claim_only_boosted_payment(&orig_caller); + let boosted_rewards_payment = + EsdtTokenPayment::new(self.reward_token_id().get(), 0, boosted_rewards); + + let new_farm_token = self.enter_farm::>(orig_caller.clone()); + self.send() + .direct_non_zero_esdt_payment(&caller, &new_farm_token); + self.send() + .direct_non_zero_esdt_payment(&caller, &boosted_rewards_payment); + self.update_energy_and_progress(&orig_caller); + + self.update_start_of_epoch_timestamp(); + + (new_farm_token, boosted_rewards_payment).into() + } + + #[payable("*")] + #[endpoint(claimRewards)] + fn claim_rewards_endpoint( + &self, + opt_orig_caller: OptionalValue, + ) -> ClaimRewardsResultType { + let caller = self.blockchain().get_caller(); + let orig_caller = self.get_orig_caller_from_opt(&caller, opt_orig_caller); + + self.migrate_old_farm_positions(&orig_caller); + + let claim_rewards_result = self.claim_rewards::>(orig_caller); + self.send() + .direct_non_zero_esdt_payment(&caller, &claim_rewards_result.new_farm_token); + self.send() + .direct_non_zero_esdt_payment(&caller, &claim_rewards_result.rewards); + + self.update_start_of_epoch_timestamp(); + + claim_rewards_result.into() + } + + #[payable("*")] + #[endpoint(compoundRewards)] + fn compound_rewards_endpoint( + &self, + opt_orig_caller: OptionalValue, + ) -> EsdtTokenPayment { + let caller = self.blockchain().get_caller(); + let orig_caller = self.get_orig_caller_from_opt(&caller, opt_orig_caller); + + self.migrate_old_farm_positions(&orig_caller); + + let output_farm_token_payment = self.compound_rewards::>(orig_caller.clone()); + self.send() + .direct_non_zero_esdt_payment(&caller, &output_farm_token_payment); + self.update_energy_and_progress(&orig_caller); + + self.update_start_of_epoch_timestamp(); + + output_farm_token_payment + } + + #[payable("*")] + #[endpoint(exitFarm)] + fn exit_farm_endpoint( + &self, + opt_orig_caller: OptionalValue, + ) -> ExitFarmWithPartialPosResultType { + let caller = self.blockchain().get_caller(); + let orig_caller = self.get_orig_caller_from_opt(&caller, opt_orig_caller); + let payment = self.call_value().single_esdt(); + let migrated_amount = self.migrate_old_farm_positions(&orig_caller); + let exit_farm_result = self.exit_farm::>(orig_caller.clone(), payment); + + self.decrease_old_farm_positions(migrated_amount, &orig_caller); + self.send() + .direct_non_zero_esdt_payment(&caller, &exit_farm_result.farming_tokens); + self.send() + .direct_non_zero_esdt_payment(&caller, &exit_farm_result.rewards); + self.clear_user_energy_if_needed(&orig_caller); + + self.update_start_of_epoch_timestamp(); + + (exit_farm_result.farming_tokens, exit_farm_result.rewards).into() + } + + #[payable("*")] + #[endpoint(mergeFarmTokens)] + fn merge_farm_tokens_endpoint( + &self, + opt_orig_caller: OptionalValue, + ) -> DoubleMultiPayment { + let caller = self.blockchain().get_caller(); + let orig_caller = self.get_orig_caller_from_opt(&caller, opt_orig_caller); + self.migrate_old_farm_positions(&orig_caller); + + let boosted_rewards = self.claim_only_boosted_payment(&orig_caller); + let boosted_rewards_payment = + EsdtTokenPayment::new(self.reward_token_id().get(), 0, boosted_rewards); + + let merged_farm_token = self.merge_and_update_farm_tokens(orig_caller); + + self.send() + .direct_non_zero_esdt_payment(&caller, &merged_farm_token); + self.send() + .direct_non_zero_esdt_payment(&caller, &boosted_rewards_payment); + + self.update_start_of_epoch_timestamp(); + + (merged_farm_token, boosted_rewards_payment).into() + } + + fn merge_and_update_farm_tokens(&self, orig_caller: ManagedAddress) -> EsdtTokenPayment { + let mut output_attributes = self.merge_and_return_attributes::>(&orig_caller); + output_attributes.original_owner = orig_caller; + + let new_token_amount = output_attributes.get_total_supply(); + self.farm_token() + .nft_create(new_token_amount, &output_attributes) + } + + #[endpoint(claimBoostedRewards)] + fn claim_boosted_rewards( + &self, + opt_user: OptionalValue, + ) -> EsdtTokenPayment { + let caller = self.blockchain().get_caller(); + let user = match &opt_user { + OptionalValue::Some(user) => user, + OptionalValue::None => &caller, + }; + if user != &caller { + require!( + self.allow_external_claim(user).get(), + "Cannot claim rewards for this address" + ); + } + + require!( + !self.user_total_farm_position(user).is_empty(), + "User total farm position is empty!" + ); + + let mut storage_cache = StorageCache::new(self); + self.validate_contract_state(storage_cache.contract_state, &storage_cache.farm_token_id); + Wrapper::::generate_aggregated_rewards(self, &mut storage_cache); + + let boosted_rewards = self.claim_only_boosted_payment(user); + let boosted_rewards_payment = + EsdtTokenPayment::new(self.reward_token_id().get(), 0, boosted_rewards); + + self.set_farm_supply_for_current_week(&storage_cache.farm_token_supply); + + self.send() + .direct_non_zero_esdt_payment(user, &boosted_rewards_payment); + + // Don't need to call update here too, the internal functions call it already + + boosted_rewards_payment + } + + #[endpoint(startProduceRewards)] + fn start_produce_rewards_endpoint(&self) { + self.require_caller_has_admin_permissions(); + self.start_produce_rewards(); + + self.update_start_of_epoch_timestamp(); + } + + #[endpoint(endProduceRewards)] + fn end_produce_rewards_endpoint(&self) { + self.require_caller_has_admin_permissions(); + self.end_produce_rewards::>(); + + self.update_start_of_epoch_timestamp(); + } + + #[endpoint(setPerBlockRewardAmount)] + fn set_per_block_rewards_endpoint(&self, per_block_amount: BigUint) { + self.require_caller_has_admin_permissions(); + self.set_per_block_rewards::>(per_block_amount); + + self.update_start_of_epoch_timestamp(); + } + + #[endpoint(setBoostedYieldsRewardsPercentage)] + fn set_boosted_yields_rewards_percentage(&self, percentage: u64) { + self.require_caller_has_admin_permissions(); + require!(percentage <= MAX_PERCENT, "Invalid percentage"); + + let mut storage_cache = StorageCache::new(self); + Wrapper::::generate_aggregated_rewards(self, &mut storage_cache); + + self.boosted_yields_rewards_percentage().set(percentage); + + self.update_start_of_epoch_timestamp(); + } + + #[view(calculateRewardsForGivenPosition)] + fn calculate_rewards_for_given_position( + &self, + user: ManagedAddress, + farm_token_amount: BigUint, + attributes: FarmTokenAttributes, + ) -> BigUint { + self.require_queried(); + + let mut storage_cache = StorageCache::new(self); + Wrapper::::generate_aggregated_rewards(self, &mut storage_cache); + + Wrapper::::calculate_rewards( + self, + &user, + &farm_token_amount, + &attributes, + &storage_cache, + ) + } +} diff --git a/dex/farm-with-top-up/testnet.toml b/dex/farm-with-top-up/testnet.toml new file mode 100644 index 000000000..e69de29bb diff --git a/dex/farm-with-top-up/wasm/Cargo.lock b/dex/farm-with-top-up/wasm/Cargo.lock new file mode 100644 index 000000000..72181851b --- /dev/null +++ b/dex/farm-with-top-up/wasm/Cargo.lock @@ -0,0 +1,567 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "common_errors" +version = "0.0.0" +dependencies = [ + "multiversx-sc", +] + +[[package]] +name = "common_structs" +version = "0.0.0" +dependencies = [ + "fixed-supply-token", + "math", + "mergeable", + "multiversx-sc", + "unwrappable", +] + +[[package]] +name = "config" +version = "0.0.0" +dependencies = [ + "common_errors", + "common_structs", + "multiversx-sc", + "pausable", + "permissions_module", +] + +[[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", +] + +[[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-boosted-yields" +version = "0.0.0" +dependencies = [ + "common_structs", + "config", + "energy-query", + "math", + "multiversx-sc", + "pausable", + "permissions_module", + "timestamp-oracle", + "utils", + "week-timekeeping", + "weekly-rewards-splitting", +] + +[[package]] +name = "farm-with-top-up" +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", + "original_owner_helper", + "pair", + "pausable", + "permissions-hub", + "permissions_hub_module", + "permissions_module", + "rewards", + "sc_whitelist_module", + "utils", + "week-timekeeping", + "weekly-rewards-splitting", +] + +[[package]] +name = "farm-with-top-up-wasm" +version = "0.0.0" +dependencies = [ + "farm-with-top-up", + "multiversx-sc-wasm-adapter", +] + +[[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", + "utils", +] + +[[package]] +name = "farm_token" +version = "0.0.0" +dependencies = [ + "common_structs", + "multiversx-sc", + "multiversx-sc-modules", + "permissions_module", +] + +[[package]] +name = "fees-collector" +version = "0.0.0" +dependencies = [ + "common_errors", + "common_structs", + "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" +dependencies = [ + "multiversx-sc", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +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" +dependencies = [ + "multiversx-sc", +] + +[[package]] +name = "mergeable" +version = "0.0.0" +dependencies = [ + "multiversx-sc", +] + +[[package]] +name = "multiversx-sc" +version = "0.53.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75ea89a26f0aacda21437a8ae5ccfbefab99d8191942b3d2eddbcbf84f9866d7" +dependencies = [ + "bitflags", + "hex-literal", + "multiversx-sc-codec", + "multiversx-sc-derive", + "num-traits", + "unwrap-infallible", +] + +[[package]] +name = "multiversx-sc-codec" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "007d7a5a8534e5dc9128cb8f15a65a21dd378e135c6016c7cd1491cd012bc8cb" +dependencies = [ + "arrayvec", + "multiversx-sc-codec-derive", + "unwrap-infallible", +] + +[[package]] +name = "multiversx-sc-codec-derive" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dffba1dce273ed5b61ee1b90aeea5c8c744617d0f12624f620768c144d83e753" +dependencies = [ + "hex", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "multiversx-sc-derive" +version = "0.53.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c17fdf90fafca2f19085ae67b0502d9f71bf8ab1be3c83808eb88e02a8c18b9" +dependencies = [ + "hex", + "proc-macro2", + "quote", + "radix_trie", + "syn", +] + +[[package]] +name = "multiversx-sc-modules" +version = "0.53.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daeb48acbd39255868a3241798df2f85050f0ae8d82d6417bd2cd0e30a241855" +dependencies = [ + "multiversx-sc", +] + +[[package]] +name = "multiversx-sc-wasm-adapter" +version = "0.53.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20659915a4377d375c46d7f237e810053a03f7e084fad6362dd5748a7233defb" +dependencies = [ + "multiversx-sc", +] + +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "original_owner_helper" +version = "0.0.0" +dependencies = [ + "common_structs", + "multiversx-sc", +] + +[[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-hub" +version = "0.0.0" +dependencies = [ + "multiversx-sc", +] + +[[package]] +name = "permissions_hub_module" +version = "0.0.0" +dependencies = [ + "multiversx-sc", + "permissions-hub", +] + +[[package]] +name = "permissions_module" +version = "0.0.0" +dependencies = [ + "bitflags", + "common_errors", + "multiversx-sc", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "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", +] + +[[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.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "syn" +version = "2.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "timestamp-oracle" +version = "0.0.0" +dependencies = [ + "common_structs", + "multiversx-sc", +] + +[[package]] +name = "token_merge_helper" +version = "0.0.0" +dependencies = [ + "common_errors", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unwrap-infallible" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "151ac09978d3c2862c4e39b557f4eceee2cc72150bc4cb4f16abf061b6e381fb" + +[[package]] +name = "unwrappable" +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_structs", + "multiversx-sc", +] + +[[package]] +name = "weekly-rewards-splitting" +version = "0.0.0" +dependencies = [ + "common_structs", + "energy-query", + "math", + "multiversx-sc", + "unwrappable", + "week-timekeeping", +] diff --git a/dex/farm-with-top-up/wasm/Cargo.toml b/dex/farm-with-top-up/wasm/Cargo.toml new file mode 100644 index 000000000..b09380dca --- /dev/null +++ b/dex/farm-with-top-up/wasm/Cargo.toml @@ -0,0 +1,34 @@ +# Code generated by the multiversx-sc build system. DO NOT EDIT. + +# ########################################## +# ############## AUTO-GENERATED ############# +# ########################################## + +[package] +name = "farm-with-top-up-wasm" +version = "0.0.0" +edition = "2021" +publish = false + +[lib] +crate-type = ["cdylib"] + +[profile.release] +codegen-units = 1 +opt-level = "z" +lto = true +debug = false +panic = "abort" +overflow-checks = false + +[profile.dev] +panic = "abort" + +[dependencies.farm-with-top-up] +path = ".." + +[dependencies.multiversx-sc-wasm-adapter] +version = "=0.53.2" + +[workspace] +members = ["."] diff --git a/dex/farm-with-top-up/wasm/src/lib.rs b/dex/farm-with-top-up/wasm/src/lib.rs new file mode 100644 index 000000000..22d108f14 --- /dev/null +++ b/dex/farm-with-top-up/wasm/src/lib.rs @@ -0,0 +1,86 @@ +// Code generated by the multiversx-sc build system. DO NOT EDIT. + +//////////////////////////////////////////////////// +////////////////// AUTO-GENERATED ////////////////// +//////////////////////////////////////////////////// + +// Init: 1 +// Upgrade: 1 +// Endpoints: 60 +// Async Callback: 1 +// Total number of exported functions: 63 + +#![no_std] + +multiversx_sc_wasm_adapter::allocator!(); +multiversx_sc_wasm_adapter::panic_handler!(); + +multiversx_sc_wasm_adapter::endpoints! { + farm_with_top_up + ( + init => init + upgrade => upgrade + enterFarm => enter_farm_endpoint + claimRewards => claim_rewards_endpoint + compoundRewards => compound_rewards_endpoint + exitFarm => exit_farm_endpoint + mergeFarmTokens => merge_farm_tokens_endpoint + claimBoostedRewards => claim_boosted_rewards + startProduceRewards => start_produce_rewards_endpoint + endProduceRewards => end_produce_rewards_endpoint + setPerBlockRewardAmount => set_per_block_rewards_endpoint + setBoostedYieldsRewardsPercentage => set_boosted_yields_rewards_percentage + calculateRewardsForGivenPosition => calculate_rewards_for_given_position + getRewardPerShare => reward_per_share + getRewardReserve => reward_reserve + getFarmingTokenId => farming_token_id + getRewardTokenId => reward_token_id + getPerBlockRewardAmount => per_block_reward_amount + getLastRewardBlockNonce => last_reward_block_nonce + getDivisionSafetyConstant => division_safety_constant + getUserTotalFarmPosition => user_total_farm_position + getAllowExternalClaim => allow_external_claim + getFarmPositionMigrationNonce => farm_position_migration_nonce + registerFarmToken => register_farm_token + getFarmTokenId => farm_token + getFarmTokenSupply => farm_token_supply + addToPauseWhitelist => add_to_pause_whitelist + removeFromPauseWhitelist => remove_from_pause_whitelist + pause => pause + resume => resume + getState => state + addAdmin => add_admin_endpoint + removeAdmin => remove_admin_endpoint + updateOwnerOrAdmin => update_owner_or_admin_endpoint + getPermissions => permissions + setPermissionsHubAddress => set_permissions_hub_address + addSCAddressToWhitelist => add_sc_address_to_whitelist + removeSCAddressFromWhitelist => remove_sc_address_from_whitelist + isSCAddressWhitelisted => is_sc_address_whitelisted + enterFarmOnBehalf => enter_farm_on_behalf + claimRewardsOnBehalf => claim_rewards_on_behalf + setBoostedYieldsFactors => set_boosted_yields_factors + getBoostedYieldsFactors => get_boosted_yields_factors + setTimestampOracleAddress => set_timestamp_oracle_address + collectUndistributedBoostedRewards => collect_undistributed_boosted_rewards + getBoostedYieldsRewardsPercentage => boosted_yields_rewards_percentage + getAccumulatedRewardsForWeek => accumulated_rewards_for_week + getFarmSupplyForWeek => farm_supply_for_week + getRemainingBoostedRewardsToDistribute => remaining_boosted_rewards_to_distribute + getUndistributedBoostedRewards => undistributed_boosted_rewards + getCurrentWeek => get_current_week + getFirstWeekStartEpoch => first_week_start_epoch + getLastActiveWeekForUser => get_last_active_week_for_user_view + getUserEnergyForWeek => get_user_energy_for_week_view + getLastGlobalUpdateWeek => last_global_update_week + getTotalRewardsForWeek => total_rewards_for_week + getTotalEnergyForWeek => total_energy_for_week + getTotalLockedTokensForWeek => total_locked_tokens_for_week + updateEnergyForUser => update_energy_for_user + getCurrentClaimProgress => current_claim_progress + setEnergyFactoryAddress => set_energy_factory_address + getEnergyFactoryAddress => energy_factory_address + ) +} + +multiversx_sc_wasm_adapter::async_callback! { farm_with_top_up } diff --git a/farm-staking/farm-staking/src/base_impl_wrapper.rs b/farm-staking/farm-staking/src/base_impl_wrapper.rs index a6a6673dd..c2384ab68 100644 --- a/farm-staking/farm-staking/src/base_impl_wrapper.rs +++ b/farm-staking/farm-staking/src/base_impl_wrapper.rs @@ -87,7 +87,6 @@ where ) -> BigUint<::Api> { let current_block_nonce = sc.blockchain().get_block_nonce(); let last_reward_nonce = sc.last_reward_block_nonce().get(); - if current_block_nonce <= last_reward_nonce { return BigUint::zero(); }