diff --git a/Cargo.lock b/Cargo.lock index 6370682..c72ec6a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -820,6 +820,34 @@ dependencies = [ "weekly-rewards-splitting", ] +[[package]] +name = "farm-boosted-rewards-subscriber" +version = "0.0.0" +dependencies = [ + "auto-farm", + "energy-factory", + "energy-query", + "hex", + "hex-literal", + "multiversx-sc", + "multiversx-sc-modules", + "multiversx-sc-scenario", + "num-bigint", + "num-traits", + "pair", + "pausable", + "subscriber", + "subscription-fee", +] + +[[package]] +name = "farm-boosted-rewards-subscriber-meta" +version = "0.0.0" +dependencies = [ + "farm-boosted-rewards-subscriber", + "multiversx-sc-meta", +] + [[package]] name = "farm-boosted-yields" version = "0.0.0" diff --git a/Cargo.toml b/Cargo.toml index ef28755..bee5334 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,7 @@ [workspace] members = [ + "farm-boosted-rewards-subscriber", + "farm-boosted-rewards-subscriber/meta", "subscriber", "subscriber/meta", "subscription-fee", diff --git a/farm-boosted-rewards-subscriber/.gitignore b/farm-boosted-rewards-subscriber/.gitignore new file mode 100644 index 0000000..2c76bc9 --- /dev/null +++ b/farm-boosted-rewards-subscriber/.gitignore @@ -0,0 +1,7 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +*/target/ + +# The mxpy output +/output*/ diff --git a/farm-boosted-rewards-subscriber/Cargo.toml b/farm-boosted-rewards-subscriber/Cargo.toml new file mode 100644 index 0000000..69913a1 --- /dev/null +++ b/farm-boosted-rewards-subscriber/Cargo.toml @@ -0,0 +1,50 @@ +[package] +name = "farm-boosted-rewards-subscriber" +version = "0.0.0" +authors = [ "you",] +edition = "2018" +publish = false + +[lib] +path = "src/lib.rs" + +[dependencies.multiversx-sc] +version = "=0.43.2" + +[dependencies.multiversx-sc-modules] +version = "=0.43.2" + +[dependencies.auto-farm] +git = "https://github.com/multiversx/mx-exchange-tools-sc" +rev = "39e355f" + +[dependencies.subscription-fee] +path = "../subscription-fee" + +[dependencies.subscriber] +path = "../subscriber" + +[dependencies.energy-query] +git = "https://github.com/multiversx/mx-exchange-sc" +rev = "7f25a72" + +[dependencies.energy-factory] +git = "https://github.com/multiversx/mx-exchange-sc" +rev = "7f25a72" + +[dependencies.pair] +git = "https://github.com/multiversx/mx-exchange-sc" +rev = "7f25a72" + +[dev-dependencies.pausable] +git = "https://github.com/multiversx/mx-exchange-sc" +rev = "7f25a72" + +[dev-dependencies.multiversx-sc-scenario] +version = "=0.43.2" + +[dev-dependencies] +num-bigint = "0.4.2" +num-traits = "0.2" +hex = "0.4" +hex-literal = "0.3.4" diff --git a/farm-boosted-rewards-subscriber/meta/Cargo.toml b/farm-boosted-rewards-subscriber/meta/Cargo.toml new file mode 100644 index 0000000..e7c5204 --- /dev/null +++ b/farm-boosted-rewards-subscriber/meta/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "farm-boosted-rewards-subscriber-meta" +version = "0.0.0" +edition = "2018" +publish = false +authors = [ "you",] + +[dev-dependencies] + +[dependencies.farm-boosted-rewards-subscriber] +path = ".." + +[dependencies.multiversx-sc-meta] +version = "=0.43.2" diff --git a/farm-boosted-rewards-subscriber/meta/src/main.rs b/farm-boosted-rewards-subscriber/meta/src/main.rs new file mode 100644 index 0000000..088b1d1 --- /dev/null +++ b/farm-boosted-rewards-subscriber/meta/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + multiversx_sc_meta::cli_main::(); +} diff --git a/farm-boosted-rewards-subscriber/multiversx.json b/farm-boosted-rewards-subscriber/multiversx.json new file mode 100644 index 0000000..7365539 --- /dev/null +++ b/farm-boosted-rewards-subscriber/multiversx.json @@ -0,0 +1,3 @@ +{ + "language": "rust" +} \ No newline at end of file diff --git a/farm-boosted-rewards-subscriber/src/buy_mex.rs b/farm-boosted-rewards-subscriber/src/buy_mex.rs new file mode 100644 index 0000000..1d3b62b --- /dev/null +++ b/farm-boosted-rewards-subscriber/src/buy_mex.rs @@ -0,0 +1,280 @@ +use auto_farm::common::unique_payments::UniquePayments; +use multiversx_sc_modules::ongoing_operation::{CONTINUE_OP, STOP_OP}; +use subscription_fee::subtract_payments::Epoch; + +multiversx_sc::imports!(); +multiversx_sc::derive_imports!(); + +pub type Percentage = u32; +pub const TOTAL_PERCENTAGE: Percentage = 10_000; +pub const BUY_MEX_COST: u64 = 20_000_000; +pub const LOCK_GAS_PER_USER: u64 = 7_000_000; + +#[derive(TypeAbi, TopEncode, TopDecode)] +pub struct MexActionsPercentages { + pub lock: Percentage, + pub fees: Percentage, + pub mex_burn: Percentage, +} + +pub struct MexActionsValue { + pub lock: BigUint, + pub fees: BigUint, + pub mex_burn: BigUint, +} + +impl MexActionsPercentages { + pub fn is_valid(&self) -> bool { + self.lock + self.fees + self.mex_burn == TOTAL_PERCENTAGE + } + + pub fn get_amounts_per_category( + &self, + total: &BigUint, + ) -> MexActionsValue { + let lock_amount = total * self.lock / TOTAL_PERCENTAGE; + let fees_amount = total * self.fees / TOTAL_PERCENTAGE; + let mex_burn_amount = total - &lock_amount - &fees_amount; + + MexActionsValue { + lock: lock_amount, + fees: fees_amount, + mex_burn: mex_burn_amount, + } + } +} + +impl MexActionsValue { + pub fn get_total_mex(&self) -> BigUint { + &self.lock + &self.mex_burn + } +} + +// dummy data is there so we don't accidentally deserialize another struct +#[derive(Default, TopEncode, TopDecode)] +pub struct MexOperationsProgress { + pub service_index: usize, + pub user_index: usize, + pub dummy_data: u8, +} + +#[multiversx_sc::module] +pub trait BuyMexModule: + subscriber::service::ServiceModule + + subscriber::common_storage::CommonStorageModule + + multiversx_sc_modules::ongoing_operation::OngoingOperationModule +{ + #[only_owner] + #[endpoint(addMexPair)] + fn add_mex_pair(&self, token_id: TokenIdentifier, pair_address: ManagedAddress) { + require!(token_id.is_valid_esdt_identifier(), "Invalid token ID"); + require!( + self.blockchain().is_smart_contract(&pair_address), + "Invalid pair address" + ); + + self.mex_pairs(&token_id).set(pair_address); + } + + #[only_owner] + #[endpoint(removeMexPair)] + fn remove_mex_pair(&self, token_id: TokenIdentifier) { + self.mex_pairs(&token_id).clear(); + } + + #[only_owner] + #[endpoint(performMexOperations)] + fn perform_mex_operations_endpoint(&self, service_index: usize) -> OperationCompletionStatus { + let actions_percentage = if service_index == 0 { + self.normal_user_percentage().get() + } else { + self.premium_user_percentage().get() + }; + + let mut gas_per_user = BUY_MEX_COST; + if actions_percentage.lock > 0 { + gas_per_user += LOCK_GAS_PER_USER; + } + + let mut progress = self.load_operation::(); + if progress.user_index == 0 { + progress.user_index = 1; + progress.service_index = service_index; + } else { + require!( + progress.service_index == service_index, + "Another operation in progress" + ); + } + + let own_address = self.blockchain().get_sc_address(); + let fees_contract_address = self.fees_contract_address().get(); + let service_id = self + .service_id() + .get_id_at_address_non_zero(&fees_contract_address, &own_address); + + let users_mapper = self.subscribed_users(service_id, service_index); + let total_users = users_mapper.len_at_address(&fees_contract_address); + + let mut total_gas_cost = 0; + let _ = self.run_while_it_has_gas(0, || { + let remaining_gas = self.blockchain().get_gas_left(); + if total_gas_cost + gas_per_user > remaining_gas || progress.user_index > total_users { + return STOP_OP; + } + + total_gas_cost += gas_per_user; + + let user_id = + users_mapper.get_by_index_at_address(&fees_contract_address, progress.user_index); + progress.user_index += 1; + + let opt_user_address = self + .user_id() + .get_address_at_address(&fees_contract_address, user_id); + if opt_user_address.is_none() { + return CONTINUE_OP; + } + + let fee_mapper = self.user_fees(service_index, user_id); + if fee_mapper.is_empty() { + return CONTINUE_OP; + } + + let fee = fee_mapper.take(); + let token_id = if fee.fees.token_identifier.is_egld() { + // TODO: Wrap egld and take cost into consideration + TokenIdentifier::from("PLACEHOLDER") + } else { + fee.fees.token_identifier.unwrap_esdt() + }; + + let user_address = unsafe { opt_user_address.unwrap_unchecked() }; + self.perform_mex_operations( + user_address, + token_id, + fee.fees.amount, + &actions_percentage, + ); + + CONTINUE_OP + }); + + if progress.user_index <= total_users { + self.save_progress(&progress); + + OperationCompletionStatus::InterruptedBeforeOutOfGas + } else { + OperationCompletionStatus::Completed + } + } + + fn perform_mex_operations( + &self, + user_address: ManagedAddress, + token_id: TokenIdentifier, + total_tokens: BigUint, + actions_percentages: &MexActionsPercentages, + ) { + let actions_value = actions_percentages.get_amounts_per_category(&total_tokens); + let total_mex_to_buy = actions_value.get_total_mex(); + + if actions_value.fees > 0 { + self.total_fees().update(|fees| { + fees.add_payment(EsdtTokenPayment::new( + token_id.clone(), + 0, + actions_value.fees.clone(), + )) + }); + } + + let bought_mex = self.buy_mex(token_id, total_mex_to_buy); + let mex_to_lock = &bought_mex.amount * actions_percentages.lock / TOTAL_PERCENTAGE; + let mex_to_burn = bought_mex.amount - &mex_to_lock; + + if mex_to_burn > 0 { + self.send() + .esdt_local_burn(&bought_mex.token_identifier, 0, &mex_to_burn); + } + + if mex_to_lock == 0 { + return; + } + + let simple_lock_address = self.simple_lock_address().get(); + let lock_period = self.lock_period().get(); + let _ = self.call_lock_tokens( + simple_lock_address, + EsdtTokenPayment::new(bought_mex.token_identifier, 0, mex_to_lock), + lock_period, + user_address, + ); + } + + fn buy_mex(&self, token_id: TokenIdentifier, amount: BigUint) -> EsdtTokenPayment { + let pair_mapper = self.mex_pairs(&token_id); + require!(!pair_mapper.is_empty(), "No pair set for token"); + + let mex_token_id = self.mex_token_id().get(); + let pair_address = pair_mapper.get(); + + self.call_swap_to_mex(pair_address, mex_token_id, token_id, amount) + } + + fn call_swap_to_mex( + &self, + pair_address: ManagedAddress, + mex_token_id: TokenIdentifier, + input_token_id: TokenIdentifier, + amount: BigUint, + ) -> EsdtTokenPayment { + self.other_pair_proxy(pair_address) + .swap_tokens_fixed_input(mex_token_id, BigUint::from(1u32)) + .with_esdt_transfer(EsdtTokenPayment::new(input_token_id, 0, amount)) + .execute_on_dest_context() + } + + fn call_lock_tokens( + &self, + simple_lock_address: ManagedAddress, + input_tokens: EsdtTokenPayment, + lock_epochs: Epoch, + destination: ManagedAddress, + ) -> EsdtTokenPayment { + self.simple_lock_proxy(simple_lock_address) + .lock_tokens_endpoint(lock_epochs, destination) + .with_esdt_transfer(input_tokens) + .execute_on_dest_context() + } + + #[proxy] + fn other_pair_proxy(&self, sc_address: ManagedAddress) -> pair::Proxy; + + #[proxy] + fn simple_lock_proxy(&self, sc_address: ManagedAddress) -> energy_factory::Proxy; + + #[storage_mapper("mexTokenId")] + fn mex_token_id(&self) -> SingleValueMapper; + + #[storage_mapper("mexPairs")] + fn mex_pairs(&self, token_id: &TokenIdentifier) -> SingleValueMapper; + + #[storage_mapper("simpleLockAddress")] + fn simple_lock_address(&self) -> SingleValueMapper; + + #[storage_mapper("lockPeriod")] + fn lock_period(&self) -> SingleValueMapper; + + #[view(getNormalUserPercentage)] + #[storage_mapper("normalUserPercentage")] + fn normal_user_percentage(&self) -> SingleValueMapper; + + #[view(getPremiumUserPercentage)] + #[storage_mapper("premiumUserPercentage")] + fn premium_user_percentage(&self) -> SingleValueMapper; + + #[view(getTotalFees)] + #[storage_mapper("totalFees")] + fn total_fees(&self) -> SingleValueMapper>; +} diff --git a/farm-boosted-rewards-subscriber/src/claim_farm_boosted.rs b/farm-boosted-rewards-subscriber/src/claim_farm_boosted.rs new file mode 100644 index 0000000..3dd75e8 --- /dev/null +++ b/farm-boosted-rewards-subscriber/src/claim_farm_boosted.rs @@ -0,0 +1,40 @@ +multiversx_sc::imports!(); +multiversx_sc::derive_imports!(); + +mod claim_farm_boosted_rewards_proxy { + multiversx_sc::imports!(); + + #[multiversx_sc::proxy] + pub trait ClaimFarmBoostedRewardsProxy { + #[endpoint(claimBoostedRewards)] + fn claim_boosted_rewards(&self, user: ManagedAddress) -> EsdtTokenPayment; + } +} + +#[derive(ManagedVecItem, Clone)] +pub struct AdditionalFarmData { + pub dummy_data: u8, +} + +#[multiversx_sc::module] +pub trait ClaimFarmBoostedRewardsModule { + fn claim_farm_boosted_rewards( + &self, + farm_address: ManagedAddress, + user: ManagedAddress, + ) -> EsdtTokenPayment { + self.farm_proxy_obj(farm_address) + .claim_boosted_rewards(user) + .execute_on_dest_context() + } + + #[proxy] + fn farm_proxy_obj( + &self, + sc_address: ManagedAddress, + ) -> claim_farm_boosted_rewards_proxy::Proxy; + + #[view(getEnergyThreshold)] + #[storage_mapper("energyThreshold")] + fn energy_threshold(&self) -> SingleValueMapper; +} diff --git a/farm-boosted-rewards-subscriber/src/lib.rs b/farm-boosted-rewards-subscriber/src/lib.rs new file mode 100644 index 0000000..6c64fb2 --- /dev/null +++ b/farm-boosted-rewards-subscriber/src/lib.rs @@ -0,0 +1,141 @@ +#![no_std] +#![feature(trait_alias)] + +use core::marker::PhantomData; + +use auto_farm::common::{address_to_id_mapper::AddressId, unique_payments::UniquePayments}; +use buy_mex::MexActionsPercentages; +use subscriber::base_functions::{AllBaseTraits, InterpretedResult, SubscriberContract}; +use subscription_fee::{service::ServiceInfo, subtract_payments::Epoch}; + +use crate::claim_farm_boosted::AdditionalFarmData; + +multiversx_sc::imports!(); +multiversx_sc::derive_imports!(); + +pub const GAS_TO_SAVE_PERFORM_ACTION_PROGRESS: u64 = 100_000; + +pub mod buy_mex; +pub mod claim_farm_boosted; + +#[multiversx_sc::contract] +pub trait SubscriberContractMain: + claim_farm_boosted::ClaimFarmBoostedRewardsModule + + buy_mex::BuyMexModule + + subscriber::base_init::BaseInitModule + + subscriber::service::ServiceModule + + subscriber::daily_operations::DailyOperationsModule + + subscriber::common_storage::CommonStorageModule + + energy_query::EnergyQueryModule + + multiversx_sc_modules::ongoing_operation::OngoingOperationModule +{ + /// Percentages must add up to 10,000 each, where 10,000 = 100% + /// Lock period is number of epochs the tokens should be locked for + #[init] + fn init( + &self, + fees_contract_address: ManagedAddress, + energy_threshold: BigUint, + mex_token_id: TokenIdentifier, + normal_user_percentages: MexActionsPercentages, + premium_user_percentages: MexActionsPercentages, + simple_lock_address: ManagedAddress, + lock_period: Epoch, + ) { + require!(mex_token_id.is_valid_esdt_identifier(), "Invalid token ID"); + require!( + normal_user_percentages.is_valid() && premium_user_percentages.is_valid(), + "Invalid percentages" + ); + require!( + self.blockchain().is_smart_contract(&simple_lock_address), + "Invalid address" + ); + + self.base_init(fees_contract_address); + self.energy_threshold().set(energy_threshold); + self.mex_token_id().set(mex_token_id); + self.normal_user_percentage().set(normal_user_percentages); + self.premium_user_percentage().set(premium_user_percentages); + self.simple_lock_address().set(simple_lock_address); + self.lock_period().set(lock_period); + + self.total_fees().set(UniquePayments::new()); + } + + #[endpoint(performAction)] + fn perform_action_endpoint( + &self, + service_index: usize, + users_to_claim: MultiValueEncoded, + ) -> OperationCompletionStatus { + require!(service_index <= 1, "invalid index"); + + let total_users = users_to_claim.len(); + let mut args_vec = ManagedVec::new(); + for _ in 0..total_users { + args_vec.push(AdditionalFarmData { dummy_data: 0 }); + } + + let own_address = self.blockchain().get_sc_address(); + let fees_contract_address = self.fees_contract_address().get(); + let service_id = self + .service_id() + .get_id_at_address_non_zero(&fees_contract_address, &own_address); + + let mut user_index = self.get_user_index(&fees_contract_address, service_id, service_index); + let run_result = self.perform_service::>( + GAS_TO_SAVE_PERFORM_ACTION_PROGRESS, + service_index, + &mut user_index, + args_vec, + ); + + self.user_index().set(user_index); + + run_result + } +} + +pub struct FarmClaimBoostedWrapper< + T: AllBaseTraits + + buy_mex::BuyMexModule + + claim_farm_boosted::ClaimFarmBoostedRewardsModule + + energy_query::EnergyQueryModule, +> { + _phantom: PhantomData, +} + +impl SubscriberContract for FarmClaimBoostedWrapper +where + T: AllBaseTraits + + buy_mex::BuyMexModule + + claim_farm_boosted::ClaimFarmBoostedRewardsModule + + energy_query::EnergyQueryModule, +{ + type SubSc = T; + type AdditionalDataType = AdditionalFarmData; + + fn perform_action( + sc: &Self::SubSc, + user_address: ManagedAddress<::Api>, + service_index: usize, + service_info: &ServiceInfo<::Api>, + _additional_data: &::AdditionalDataType, + ) -> Result::Api>, ()> { + if service_index == 1 { + let user_energy = sc.get_energy_amount(&user_address); + let energy_threshold = sc.energy_threshold().get(); + if user_energy < energy_threshold { + return Result::Err(()); + } + } + + let _ = sc.claim_farm_boosted_rewards(service_info.sc_address.clone(), user_address); + + // farm already sent rewards to user + Result::Ok(InterpretedResult { + user_rewards: ManagedVec::new(), + }) + } +} diff --git a/farm-boosted-rewards-subscriber/wasm/Cargo.lock b/farm-boosted-rewards-subscriber/wasm/Cargo.lock new file mode 100644 index 0000000..f101bfe --- /dev/null +++ b/farm-boosted-rewards-subscriber/wasm/Cargo.lock @@ -0,0 +1,209 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", +] + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "empty" +version = "0.0.0" +dependencies = [ + "multiversx-sc", +] + +[[package]] +name = "empty-wasm" +version = "0.0.0" +dependencies = [ + "empty", + "multiversx-sc-wasm-adapter", +] + +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[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.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" + +[[package]] +name = "multiversx-sc" +version = "0.43.2" +dependencies = [ + "bitflags", + "hashbrown", + "hex-literal", + "multiversx-sc-codec", + "multiversx-sc-derive", + "num-traits", +] + +[[package]] +name = "multiversx-sc-codec" +version = "0.18.1" +dependencies = [ + "arrayvec", + "multiversx-sc-codec-derive", +] + +[[package]] +name = "multiversx-sc-codec-derive" +version = "0.18.1" +dependencies = [ + "hex", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "multiversx-sc-derive" +version = "0.43.2" +dependencies = [ + "hex", + "proc-macro2", + "quote", + "radix_trie", + "syn", +] + +[[package]] +name = "multiversx-sc-wasm-adapter" +version = "0.43.2" +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.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "proc-macro2" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +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 = "smallvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" diff --git a/farm-boosted-rewards-subscriber/wasm/Cargo.toml b/farm-boosted-rewards-subscriber/wasm/Cargo.toml new file mode 100644 index 0000000..16f45f6 --- /dev/null +++ b/farm-boosted-rewards-subscriber/wasm/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "farm-boosted-rewards-subscriber-wasm" +version = "0.0.0" +edition = "2018" +publish = false +authors = [ "you",] + +[lib] +crate-type = [ "cdylib",] + +[workspace] +members = [ ".",] + +[dev-dependencies] + +[profile.release] +codegen-units = 1 +opt-level = "z" +lto = true +debug = false +panic = "abort" + +[dependencies.farm-boosted-rewards-subscriber] +path = ".." + +[dependencies.multiversx-sc-wasm-adapter] +version = "=0.43.2" diff --git a/farm-boosted-rewards-subscriber/wasm/src/lib.rs b/farm-boosted-rewards-subscriber/wasm/src/lib.rs new file mode 100644 index 0000000..8ea5e5e --- /dev/null +++ b/farm-boosted-rewards-subscriber/wasm/src/lib.rs @@ -0,0 +1,26 @@ +// Code generated by the multiversx-sc multi-contract system. DO NOT EDIT. + +//////////////////////////////////////////////////// +////////////////// AUTO-GENERATED ////////////////// +//////////////////////////////////////////////////// + +// Init: 1 +// Endpoints: 0 +// Async Callback (empty): 1 +// Total number of exported functions: 2 + +#![no_std] +#![allow(internal_features)] +#![feature(lang_items)] + +multiversx_sc_wasm_adapter::allocator!(); +multiversx_sc_wasm_adapter::panic_handler!(); + +multiversx_sc_wasm_adapter::endpoints! { + empty + ( + init => init + ) +} + +multiversx_sc_wasm_adapter::async_callback_empty! {} diff --git a/subscriber/src/base_functions.rs b/subscriber/src/base_functions.rs index cd68a8c..ac2026f 100644 --- a/subscriber/src/base_functions.rs +++ b/subscriber/src/base_functions.rs @@ -1,4 +1,3 @@ -use auto_farm::common::address_to_id_mapper::AddressId; use subscription_fee::service::ServiceInfo; multiversx_sc::imports!(); @@ -21,7 +20,6 @@ pub trait SubscriberContract { fn perform_action( sc: &Self::SubSc, user_address: ManagedAddress<::Api>, - user_id: AddressId, service_index: usize, service_info: &ServiceInfo<::Api>, additional_data: &::AdditionalDataType, diff --git a/subscriber/src/daily_operations.rs b/subscriber/src/daily_operations.rs index 2ecdab8..b2dde02 100644 --- a/subscriber/src/daily_operations.rs +++ b/subscriber/src/daily_operations.rs @@ -3,10 +3,7 @@ use core::borrow::Borrow; use auto_farm::common::address_to_id_mapper::AddressId; use multiversx_sc::api::StorageMapperApi; use multiversx_sc_modules::ongoing_operation::{LoopOp, CONTINUE_OP, STOP_OP}; -use subscription_fee::{ - service::ServiceInfo, - subtract_payments::{Epoch, MyVeryOwnScResult, ProxyTrait as _}, -}; +use subscription_fee::service::ServiceInfo; use crate::base_functions::SubscriberContract; @@ -14,12 +11,11 @@ multiversx_sc::imports!(); multiversx_sc::derive_imports!(); pub const FIRST_INDEX: usize = 1; -pub const GAS_TO_SAVE_PROGRESS: u64 = 100_000; #[derive(TypeAbi, TopEncode, TopDecode, Default)] -pub struct OperationProgress { +pub struct ClaimRewardsOperationProgress { pub service_index: usize, - pub current_index: usize, + pub user_index: usize, pub additional_data_index: usize, } @@ -45,6 +41,7 @@ pub trait DailyOperationsModule: { fn perform_service>( &self, + gas_to_save_progress: u64, service_index: usize, user_index: &mut usize, additional_data: ManagedVec, @@ -55,12 +52,10 @@ pub trait DailyOperationsModule: .service_id() .get_id_at_address_non_zero(&fees_contract_address, &own_address); - let users_mapper = self.subscribed_users(service_id, service_index); - let total_users = users_mapper.len_at_address(&fees_contract_address); - let mut progress = self.load_operation::(); - if progress.current_index == 0 { + let mut progress = self.load_operation::(); + if progress.user_index == 0 { progress.service_index = service_index; - progress.current_index = *user_index; + progress.user_index = *user_index; progress.additional_data_index = 0; } else { require!( @@ -69,6 +64,9 @@ pub trait DailyOperationsModule: ); } + let users_mapper = self.subscribed_users(service_id, service_index); + let total_users = users_mapper.len_at_address(&fees_contract_address); + let mut all_data = OperationData:: { additional_data_len: additional_data.len(), additional_data, @@ -83,44 +81,26 @@ pub trait DailyOperationsModule: fees_contract_address, }; - let mut output_egld = BigUint::zero(); - let mut output_esdt = ManagedVec::new(); - - let run_result = self.run_while_it_has_gas(GAS_TO_SAVE_PROGRESS, || { - self.perform_one_operation::( - &mut progress, - &mut all_data, - &mut output_egld, - &mut output_esdt, - ) + let run_result = self.run_while_it_has_gas(gas_to_save_progress, || { + self.perform_one_operation::(&mut progress, &mut all_data) }); if run_result == OperationCompletionStatus::InterruptedBeforeOutOfGas { self.save_progress(&progress); } - *user_index = progress.current_index; - - let caller = self.blockchain().get_caller(); - if output_egld > 0 { - self.send().direct_egld(&caller, &output_egld); - } - if !output_esdt.is_empty() { - self.send().direct_multi(&caller, &output_esdt); - } + *user_index = progress.user_index; run_result } fn perform_one_operation>( &self, - progress: &mut OperationProgress, + progress: &mut ClaimRewardsOperationProgress, all_data: &mut OperationData, - output_egld: &mut BigUint, - output_esdt: &mut ManagedVec, ) -> LoopOp { if progress.additional_data_index >= all_data.additional_data_len - || progress.current_index > all_data.total_users + || progress.user_index > all_data.total_users { return STOP_OP; } @@ -130,7 +110,7 @@ pub trait DailyOperationsModule: let user_id = all_data .users_mapper - .get_by_index_at_address(&all_data.fees_contract_address, progress.current_index); + .get_by_index_at_address(&all_data.fees_contract_address, progress.user_index); let opt_user_address = self .user_id() .get_address_at_address(&all_data.fees_contract_address, user_id); @@ -141,54 +121,22 @@ pub trait DailyOperationsModule: } }; - progress.current_index += 1; - - let subtract_result = self.call_subtract_payment( - all_data.fees_contract_address.clone(), - all_data.service_index, - user_id, - ); - if subtract_result.is_err() { - return CONTINUE_OP; - } - - let fee = unsafe { subtract_result.unwrap_unchecked() }; - if fee.token_identifier.is_egld() { - *output_egld += fee.amount; - } else { - let payment = EsdtTokenPayment::new(fee.token_identifier.unwrap_esdt(), 0, fee.amount); - output_esdt.push(payment); - } + progress.user_index += 1; let action_results = SC::perform_action( self, user_address.clone(), - user_id, all_data.service_index, &all_data.service_info, user_data.borrow(), ); - if action_results.is_err() { - return CONTINUE_OP; + if let Result::Ok(interpreted_results) = action_results { + self.send_user_rewards(&user_address, interpreted_results.user_rewards); } - let interpreted_results = unsafe { action_results.unwrap_unchecked() }; - self.send_user_rewards(&user_address, interpreted_results.user_rewards); - CONTINUE_OP } - fn call_subtract_payment( - &self, - fee_contract_address: ManagedAddress, - service_index: usize, - user_id: AddressId, - ) -> MyVeryOwnScResult { - self.fee_contract_proxy_obj(fee_contract_address) - .subtract_payment(service_index, user_id) - .execute_on_dest_context() - } - fn send_user_rewards( &self, user_address: &ManagedAddress, @@ -199,10 +147,19 @@ pub trait DailyOperationsModule: } } - fn get_user_index(&self, service_index: usize, current_epoch: Epoch) -> usize { - let last_action_epoch = self.last_global_action_epoch(service_index).get(); - if last_action_epoch == current_epoch { - self.user_index().get() + fn get_user_index( + &self, + fees_contract_address: &ManagedAddress, + service_id: AddressId, + service_index: usize, + ) -> usize { + let last_user_index = self + .subscribed_users(service_id, service_index) + .len_at_address(fees_contract_address); + let stored_user_index = self.user_index().get(); + + if stored_user_index != 0 && stored_user_index < last_user_index { + stored_user_index } else { 1 } @@ -210,13 +167,4 @@ pub trait DailyOperationsModule: #[storage_mapper("userIndex")] fn user_index(&self) -> SingleValueMapper; - - #[storage_mapper("lastGloblalActionEpoch")] - fn last_global_action_epoch(&self, service_index: usize) -> SingleValueMapper; - - #[proxy] - fn fee_contract_proxy_obj( - &self, - sc_address: ManagedAddress, - ) -> subscription_fee::Proxy; } diff --git a/subscriber/src/lib.rs b/subscriber/src/lib.rs index bd105e3..a8881a8 100644 --- a/subscriber/src/lib.rs +++ b/subscriber/src/lib.rs @@ -3,12 +3,11 @@ use core::marker::PhantomData; -use auto_farm::common::address_to_id_mapper::AddressId; use base_functions::{AllBaseTraits, InterpretedResult, SubscriberContract}; -use multiversx_sc::derive::ManagedVecItem; use subscription_fee::service::ServiceInfo; multiversx_sc::imports!(); +multiversx_sc::derive_imports!(); pub mod base_functions; pub mod base_init; @@ -16,6 +15,8 @@ pub mod common_storage; pub mod daily_operations; pub mod service; +pub const DEFAULT_GAS_TO_SAVE_PROGRESS: u64 = 100_000; + #[multiversx_sc::contract] pub trait SubscriberContractMain: base_init::BaseInitModule @@ -30,22 +31,24 @@ pub trait SubscriberContractMain: } fn dummy_perform_action(&self, service_index: usize) -> OperationCompletionStatus { - let current_epoch = self.blockchain().get_block_epoch(); - let mut user_index = self.get_user_index(service_index, current_epoch); + let fees_contract_address = self.fees_contract_address().get(); + let mut user_index = self.get_user_index(&fees_contract_address, 1, service_index); let mut dummy_args = ManagedVec::new(); for _ in 0..10 { dummy_args.push(DummyData { dummy_data: 0 }); } - let result = - self.perform_service::>(service_index, &mut user_index, dummy_args); + let run_result = self.perform_service::>( + DEFAULT_GAS_TO_SAVE_PROGRESS, + service_index, + &mut user_index, + dummy_args, + ); self.user_index().set(user_index); - self.last_global_action_epoch(service_index) - .set(current_epoch); - result + run_result } } @@ -68,7 +71,6 @@ where fn perform_action( _sc: &Self::SubSc, _user_address: ManagedAddress<::Api>, - _user_id: AddressId, _service_index: usize, _service_info: &ServiceInfo<::Api>, _additional_data: &::AdditionalDataType, diff --git a/subscriber/src/service.rs b/subscriber/src/service.rs index 3f7f65a..71c9304 100644 --- a/subscriber/src/service.rs +++ b/subscriber/src/service.rs @@ -1,10 +1,42 @@ -use subscription_fee::service::ProxyTrait as _; +use auto_farm::common::address_to_id_mapper::AddressId; +use multiversx_sc::api::StorageMapperApi; +use multiversx_sc_modules::ongoing_operation::{LoopOp, CONTINUE_OP, STOP_OP}; +use subscription_fee::{ + service::ProxyTrait as _, + subtract_payments::{Epoch, MyVeryOwnScResult, ProxyTrait as _, MONTHLY_EPOCHS}, +}; multiversx_sc::imports!(); multiversx_sc::derive_imports!(); +// Careful to not have this be the exact same size as ClaimRewardsOperationProgress struct!!! +#[derive(TypeAbi, TopEncode, TopDecode, Default)] +pub struct SubtractPaymentOperation { + pub service_index: usize, + pub user_index: usize, +} + +#[derive(TypeAbi, TopEncode, TopDecode)] +pub struct UserFees { + pub fees: EgldOrEsdtTokenPayment, + pub epoch: Epoch, +} + +pub struct PaymentOperationData { + pub total_users: usize, + pub service_index: usize, + pub current_epoch: Epoch, + pub fees_contract_address: ManagedAddress, + pub users_mapper: UnorderedSetMapper, +} + +pub const GAS_TO_SAVE_PAYMENT_PROGRESS: u64 = 200_000; + #[multiversx_sc::module] -pub trait ServiceModule: crate::common_storage::CommonStorageModule { +pub trait ServiceModule: + crate::common_storage::CommonStorageModule + + multiversx_sc_modules::ongoing_operation::OngoingOperationModule +{ /// Arguments are pairs of sc_address, opt_payment_token and payment_amount #[only_owner] #[endpoint(registerService)] @@ -31,9 +63,155 @@ pub trait ServiceModule: crate::common_storage::CommonStorageModule { .execute_on_dest_context(); } + #[only_owner] + #[endpoint(subtractPayment)] + fn subtract_payment_endpoint(&self, service_index: usize) -> OperationCompletionStatus { + let own_address = self.blockchain().get_sc_address(); + let fees_contract_address = self.fees_contract_address().get(); + let service_id = self + .service_id() + .get_id_at_address_non_zero(&fees_contract_address, &own_address); + let current_epoch = self.blockchain().get_block_epoch(); + let next_sub_epoch = self.next_subtract_epoch(service_index).get(); + require!( + current_epoch >= next_sub_epoch, + "Cannot subtract payment yet!" + ); + + let user_index = + self.get_subtract_user_index(&fees_contract_address, service_id, service_index); + let mut progress = self.load_operation::(); + if progress.user_index == 0 { + progress.service_index = service_index; + progress.user_index = user_index; + } else { + require!( + progress.service_index == service_index, + "Another operation is in progress" + ); + } + + let users_mapper = self.subscribed_users(service_id, service_index); + let total_users = users_mapper.len_at_address(&fees_contract_address); + let all_data = PaymentOperationData { + current_epoch, + fees_contract_address, + service_index, + total_users, + users_mapper, + }; + + let run_result = self.run_while_it_has_gas(GAS_TO_SAVE_PAYMENT_PROGRESS, || { + self.perform_one_sub_operation(&mut progress, &all_data) + }); + + match run_result { + OperationCompletionStatus::Completed => self + .next_subtract_epoch(service_index) + .set(current_epoch + MONTHLY_EPOCHS), + OperationCompletionStatus::InterruptedBeforeOutOfGas => self.save_progress(&progress), + } + + self.subtract_user_index().set(progress.user_index); + + run_result + } + + fn perform_one_sub_operation( + &self, + progress: &mut SubtractPaymentOperation, + all_data: &PaymentOperationData, + ) -> LoopOp { + if progress.user_index > all_data.total_users { + self.next_subtract_epoch(all_data.service_index) + .set(all_data.current_epoch + MONTHLY_EPOCHS); + + return STOP_OP; + } + + let user_id = all_data + .users_mapper + .get_by_index_at_address(&all_data.fees_contract_address, progress.user_index); + let opt_user_address = self + .user_id() + .get_address_at_address(&all_data.fees_contract_address, user_id); + if opt_user_address.is_none() { + return CONTINUE_OP; + } + + progress.user_index += 1; + + let fees_mapper = self.user_fees(all_data.service_index, user_id); + require!(fees_mapper.is_empty(), "Last fees not processed yet"); + + let subtract_result = self.call_subtract_payment( + all_data.fees_contract_address.clone(), + all_data.service_index, + user_id, + ); + if let MyVeryOwnScResult::Ok(fees) = subtract_result { + let user_fees = UserFees { + fees, + epoch: all_data.current_epoch, + }; + + fees_mapper.set(user_fees); + } + + CONTINUE_OP + } + + fn get_subtract_user_index( + &self, + fees_contract_address: &ManagedAddress, + service_id: AddressId, + service_index: usize, + ) -> usize { + let last_user_index = self + .subscribed_users(service_id, service_index) + .len_at_address(fees_contract_address); + let stored_user_index = self.subtract_user_index().get(); + + if stored_user_index != 0 && stored_user_index <= last_user_index { + stored_user_index + } else { + 1 + } + } + + fn call_subtract_payment( + &self, + fee_contract_address: ManagedAddress, + service_index: usize, + user_id: AddressId, + ) -> MyVeryOwnScResult { + self.fee_contract_proxy_obj(fee_contract_address) + .subtract_payment(service_index, user_id) + .execute_on_dest_context() + } + #[proxy] fn register_service_proxy_obj( &self, sc_address: ManagedAddress, ) -> subscription_fee::Proxy; + + #[proxy] + fn fee_contract_proxy_obj( + &self, + sc_address: ManagedAddress, + ) -> subscription_fee::Proxy; + + #[storage_mapper("subtractUserIndex")] + fn subtract_user_index(&self) -> SingleValueMapper; + + #[storage_mapper("nextSubtractEpoch")] + fn next_subtract_epoch(&self, service_index: usize) -> SingleValueMapper; + + #[storage_mapper("userFees")] + fn user_fees( + &self, + service_index: usize, + user_id: AddressId, + ) -> SingleValueMapper>; } diff --git a/subscriber/tests/subscriber_setup/mod.rs b/subscriber/tests/subscriber_setup/mod.rs index a22dcc4..8afb917 100644 --- a/subscriber/tests/subscriber_setup/mod.rs +++ b/subscriber/tests/subscriber_setup/mod.rs @@ -87,6 +87,17 @@ where ) } + pub fn call_subtract_payment(&mut self, service_index: usize) -> TxResult { + self.b_mock.borrow_mut().execute_tx( + &self.owner_addr, + &self.sub_wrapper, + &rust_biguint!(0), + |sc| { + sc.subtract_payment_endpoint(service_index); + }, + ) + } + pub fn call_perform_action(&mut self, service_index: usize) -> TxResult { self.b_mock.borrow_mut().execute_tx( &self.owner_addr, diff --git a/subscriber/tests/subscriber_test.rs b/subscriber/tests/subscriber_test.rs index 31399e8..bef9472 100644 --- a/subscriber/tests/subscriber_test.rs +++ b/subscriber/tests/subscriber_test.rs @@ -59,7 +59,7 @@ fn init_all< b_mock_rc .borrow_mut() .execute_tx(&owner, &sub_sc.s_wrapper, &rust_biguint!(0), |sc| { - sc.add_pair( + sc.add_usdc_pair( managed_token_id!(FIRST_TOKEN_ID), managed_address!(pair_setup.pair_wrapper.address_ref()), ); @@ -275,12 +275,13 @@ fn perform_daily_action_test() { b_mock_rc.borrow_mut().set_block_epoch(10); + subscriber_setup.call_subtract_payment(0).assert_ok(); subscriber_setup.call_perform_action(0).assert_ok(); b_mock_rc.borrow().check_esdt_balance( - &subscriber_setup.owner_addr, + subscriber_setup.sub_wrapper.address_ref(), FIRST_TOKEN_ID, - &rust_biguint!(1_000), + &rust_biguint!(1_000 * 30), ); // try perform operation again, same epoch @@ -288,18 +289,19 @@ fn perform_daily_action_test() { // still same balance, no funds subtracted b_mock_rc.borrow().check_esdt_balance( - &subscriber_setup.owner_addr, + subscriber_setup.sub_wrapper.address_ref(), FIRST_TOKEN_ID, - &rust_biguint!(1_000), + &rust_biguint!(1_000 * 30), ); b_mock_rc.borrow_mut().set_block_epoch(11); subscriber_setup.call_perform_action(0).assert_ok(); + // still same balance, subtraction is done manually once per month b_mock_rc.borrow().check_esdt_balance( - &subscriber_setup.owner_addr, + subscriber_setup.sub_wrapper.address_ref(), FIRST_TOKEN_ID, - &rust_biguint!(2_000), + &rust_biguint!(1_000 * 30), ); } diff --git a/subscription-fee/src/pair_actions.rs b/subscription-fee/src/pair_actions.rs index 1c51abe..f554a2a 100644 --- a/subscription-fee/src/pair_actions.rs +++ b/subscription-fee/src/pair_actions.rs @@ -15,8 +15,8 @@ pub mod pair_proxy { #[multiversx_sc::module] pub trait PairActionsModule { #[only_owner] - #[endpoint(addPair)] - fn add_pair(&self, token_id: TokenIdentifier, pair_address: ManagedAddress) { + #[endpoint(addUsdcPair)] + fn add_usdc_pair(&self, token_id: TokenIdentifier, pair_address: ManagedAddress) { require!(token_id.is_valid_esdt_identifier(), "Invalid token ID"); require!( self.blockchain().is_smart_contract(&pair_address), @@ -27,8 +27,8 @@ pub trait PairActionsModule { } #[only_owner] - #[endpoint(removePair)] - fn remove_pair(&self, token_id: TokenIdentifier) { + #[endpoint(removeUsdcPair)] + fn remove_usdc_pair(&self, token_id: TokenIdentifier) { self.pair_address_for_token(&token_id).clear(); } diff --git a/subscription-fee/src/subtract_payments.rs b/subscription-fee/src/subtract_payments.rs index 40b9d40..e762770 100644 --- a/subscription-fee/src/subtract_payments.rs +++ b/subscription-fee/src/subtract_payments.rs @@ -56,15 +56,13 @@ pub trait SubtractPaymentsModule: ) -> MyVeryOwnScResult { let caller = self.blockchain().get_caller(); let service_id = self.service_id().get_id_non_zero(&caller); - - let subscription_type = self - .subscription_type(service_id, user_id, service_index) - .get(); - let next_action_epoch = - self.get_next_action_epoch(user_id, service_id, service_index, subscription_type); let current_epoch = self.blockchain().get_block_epoch(); - if next_action_epoch > current_epoch { - return MyVeryOwnScResult::Err(()); + + let last_action_mapper = self.user_last_action_epoch(user_id, service_id, service_index); + let last_action_epoch = last_action_mapper.get(); + if last_action_epoch > 0 { + let next_subtract_epoch = last_action_epoch + MONTHLY_EPOCHS; + require!(next_subtract_epoch <= current_epoch, "Cannot subtract yet"); } let opt_user_address = self.user_id().get_address(user_id); @@ -72,10 +70,22 @@ pub trait SubtractPaymentsModule: return MyVeryOwnScResult::Err(()); } + let subscription_type = self + .subscription_type(user_id, service_id, service_index) + .get(); + let multiplier = match subscription_type { + SubscriptionType::Daily => MONTHLY_EPOCHS / DAILY_EPOCHS, + SubscriptionType::Weekly => MONTHLY_EPOCHS / WEEKLY_EPOCHS, + SubscriptionType::Monthly => 1, + SubscriptionType::None => return MyVeryOwnScResult::Err(()), + }; + let service_info = self.service_info(service_id).get().get(service_index); let subtract_result = match service_info.opt_payment_token { - Some(token_id) => self.subtract_specific_token(user_id, token_id, service_info.amount), - None => self.subtract_any_token(user_id, service_info.amount), + Some(token_id) => { + self.subtract_specific_token(user_id, token_id, service_info.amount * multiplier) + } + None => self.subtract_any_token(user_id, service_info.amount * multiplier), }; if let MyVeryOwnScResult::Ok(payment) = &subtract_result { self.send().direct( @@ -86,8 +96,7 @@ pub trait SubtractPaymentsModule: ); } - self.last_action_epoch(user_id, service_id, service_index) - .set(current_epoch); + last_action_mapper.set(current_epoch); subtract_result } @@ -162,26 +171,8 @@ pub trait SubtractPaymentsModule: MyVeryOwnScResult::Err(()) } - fn get_next_action_epoch( - &self, - user_id: AddressId, - service_id: AddressId, - service_index: usize, - subscription_type: SubscriptionType, - ) -> Epoch { - let last_action_epoch = self - .last_action_epoch(user_id, service_id, service_index) - .get(); - match subscription_type { - SubscriptionType::None => sc_panic!("Unexpected value"), - SubscriptionType::Daily => last_action_epoch + DAILY_EPOCHS, - SubscriptionType::Weekly => last_action_epoch + WEEKLY_EPOCHS, - SubscriptionType::Monthly => last_action_epoch + MONTHLY_EPOCHS, - } - } - - #[storage_mapper("lastActionEpoch")] - fn last_action_epoch( + #[storage_mapper("userLastActionEpoch")] + fn user_last_action_epoch( &self, user_id: AddressId, service_id: AddressId, diff --git a/subscription-fee/tests/pair_setup/mod.rs b/subscription-fee/tests/pair_setup/mod.rs index cd57878..e133c72 100644 --- a/subscription-fee/tests/pair_setup/mod.rs +++ b/subscription-fee/tests/pair_setup/mod.rs @@ -29,6 +29,7 @@ impl PairSetup where PairObjBuilder: 'static + Copy + Fn() -> pair::ContractObj, { + #[allow(clippy::too_many_arguments)] pub fn new( b_mock: Rc>, pair_builder: PairObjBuilder, diff --git a/subscription-fee/tests/subscription_setup/mod.rs b/subscription-fee/tests/subscription_setup/mod.rs index ac05060..de7b1ce 100644 --- a/subscription-fee/tests/subscription_setup/mod.rs +++ b/subscription-fee/tests/subscription_setup/mod.rs @@ -42,7 +42,7 @@ where let rust_zero = rust_biguint!(0); let s_wrapper = b_mock.borrow_mut().create_sc_account( &rust_zero, - Some(&owner_addr), + Some(owner_addr), builder, "some wasm path", ); diff --git a/subscription-fee/tests/subscription_test.rs b/subscription-fee/tests/subscription_test.rs index 32fc3bc..4959c48 100644 --- a/subscription-fee/tests/subscription_test.rs +++ b/subscription-fee/tests/subscription_test.rs @@ -47,14 +47,14 @@ fn init_all< b_mock_rc.clone(), sub_builder, &owner, - &pair_setup.pair_wrapper.address_ref(), + pair_setup.pair_wrapper.address_ref(), vec![FIRST_TOKEN_ID.to_vec()], ); b_mock_rc .borrow_mut() .execute_tx(&owner, &sub_sc.s_wrapper, &rust_biguint!(0), |sc| { - sc.add_pair( + sc.add_usdc_pair( managed_token_id!(FIRST_TOKEN_ID), managed_address!(pair_setup.pair_wrapper.address_ref()), ); @@ -260,9 +260,11 @@ fn subtract_ok_test() { .call_subtract_payment(&rand_service, 0, 1) .assert_ok(); - b_mock_rc - .borrow() - .check_esdt_balance(&rand_service, FIRST_TOKEN_ID, &rust_biguint!(1_000)); + b_mock_rc.borrow().check_esdt_balance( + &rand_service, + FIRST_TOKEN_ID, + &rust_biguint!(30 * 1_000), + ); } #[test] @@ -304,18 +306,22 @@ fn try_subtract_twice_same_day() { .call_subtract_payment(&rand_service, 0, 1) .assert_ok(); - b_mock_rc - .borrow() - .check_esdt_balance(&rand_service, FIRST_TOKEN_ID, &rust_biguint!(1_000)); + b_mock_rc.borrow().check_esdt_balance( + &rand_service, + FIRST_TOKEN_ID, + &rust_biguint!(30 * 1_000), + ); sub_sc .call_subtract_payment(&rand_service, 0, 1) - .assert_ok(); + .assert_user_error("Cannot subtract yet"); // still same balance - b_mock_rc - .borrow() - .check_esdt_balance(&rand_service, FIRST_TOKEN_ID, &rust_biguint!(1_000)); + b_mock_rc.borrow().check_esdt_balance( + &rand_service, + FIRST_TOKEN_ID, + &rust_biguint!(30 * 1_000), + ); } #[test] @@ -353,10 +359,12 @@ fn any_token_subtract_fee_test() { .call_subtract_payment(&rand_service, 0, 1) .assert_ok(); - // pair has 1:2 token ratio, so for 1_000 tokens, we get 2_000 of the other - b_mock_rc - .borrow() - .check_esdt_balance(&rand_service, FIRST_TOKEN_ID, &rust_biguint!(2_000)); + // pair has 1:2 token ratio, so for 30 * 1_000 tokens, we get 30 * 2_000 of the other + b_mock_rc.borrow().check_esdt_balance( + &rand_service, + FIRST_TOKEN_ID, + &rust_biguint!(30 * 2_000), + ); } #[test]