From 6b14ab761df52bc2482c2fbff231e713282c5a5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Tue, 18 Oct 2022 10:31:15 +0300 Subject: [PATCH 01/20] Cache fees to energy contract Use sendFeesToCollector to send them to collector (by weekly basis) --- .../src/unlock_with_penalty.rs | 48 +++++++++++++++---- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs b/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs index f40d419e4..4571b888d 100644 --- a/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs +++ b/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs @@ -1,7 +1,7 @@ elrond_wasm::imports!(); elrond_wasm::derive_imports!(); -use common_structs::Epoch; +use common_structs::{Epoch, Nonce}; use simple_lock::locked_token::LockedTokenAttributes; use crate::lock_options::{EPOCHS_PER_YEAR}; @@ -24,7 +24,11 @@ pub mod fees_collector_proxy { #[payable("*")] #[endpoint(depositSwapFees)] fn deposit_swap_fees(&self); - } + + #[payable("*")] + #[endpoint(depositPenaltyFees)] + fn deposit_penalty_fees(&self); + } #[elrond_wasm::module] @@ -142,7 +146,7 @@ pub trait UnlockWithPenaltyModule: "No tokens remaining after penalty is applied" ); - self.burn_penalty(unlocked_token_id, &penalty_amount); + self.burn_penalty(unlocked_token_id, &payment.token_nonce, &penalty_amount); } let output_payment = self.lock_and_send(&caller, unlocked_tokens, new_unlock_epoch); @@ -197,24 +201,48 @@ pub trait UnlockWithPenaltyModule: token_amount * penalty_percentage / MAX_PERCENTAGE as u64 } - fn burn_penalty(&self, token_id: TokenIdentifier, fees_amount: &BigUint) { + fn burn_penalty(&self, token_id: TokenIdentifier, token_nonce: Nonce, fees_amount: &BigUint) { let fees_burn_percentage = self.fees_burn_percentage().get(); let burn_amount = fees_amount * fees_burn_percentage as u64 / MAX_PERCENTAGE as u64; let remaining_amount = fees_amount - &burn_amount; + let locked_token_mapper = self.locked_token(); if burn_amount > 0 { self.send().esdt_local_burn(&token_id, 0, &burn_amount); } if remaining_amount > 0 { - self.send_fees_to_collector(token_id, remaining_amount); + // self.send_fees_to_collector(token_id, remaining_amount); + if self.fees_from_penalty_unlocking().is_empty() { + self.fees_from_penalty_unlocking() + .set(token_nonce, remaining_amount); + } else { + let (existing_nonce, existing_amount) = self.fees_from_penalty_unlocking().get(); + let new_token_attributes: LockedTokenAttributes = + locked_token_mapper.get_token_attributes(token_nonce); + let new_amount = existing_amount + remaining_amount; + + locked_token_mapper.nft_burn(existing_nonce, existing_amount); + locked_token_mapper.nft_burn(token_nonce, remaining_amount); + + let new_token_payment = + locked_token_mapper.nft_create(new_amount, new_token_attributes); + self.fees_from_penalty_unlocking() + .set(new_token_payment.token_nonce, new_token_payment.amount); + } } } + #[payable("*")] + #[endpoint(sendFeesToCollector)] fn send_fees_to_collector(&self, token_id: TokenIdentifier, amount: BigUint) { let sc_address = self.fees_collector_address().get(); - self.fees_collector_proxy_builder(sc_address) - .deposit_swap_fees() - .add_esdt_token_transfer(token_id, 0, amount) + let locked_token_id = self.locked_token().get_token_id(); + let (nonce, amount) = self.fees_from_penalty_unlocking().get(); + + let fees_amount = self + .fees_collector_proxy_builder(sc_address) + .deposit_penalty_fees() + .add_esdt_token_transfer(locked_token_id, nonce, amount) .execute_on_dest_context_ignore_result(); } @@ -235,4 +263,8 @@ pub trait UnlockWithPenaltyModule: #[view(getFeesCollectorAddress)] #[storage_mapper("feesCollectorAddress")] fn fees_collector_address(&self) -> SingleValueMapper; + + #[view(getFeesFromPenaltyUnlocking)] + #[storage_mapper("fees_from_penalty_unlocking")] + fn fees_from_penalty_unlocking(&self) -> SingleValueMapper; } From 69d3c925a9b937e74fb56ebf205024cc0315e266 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Tue, 18 Oct 2022 11:02:01 +0300 Subject: [PATCH 02/20] Cache the loked tokens into storage Use fees_from_penalty_unlocking storage to cache fees --- common/common_structs/src/wrapper_types.rs | 14 ++++++ .../src/unlock_with_penalty.rs | 44 +++++++++++-------- 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/common/common_structs/src/wrapper_types.rs b/common/common_structs/src/wrapper_types.rs index c1795a796..7f3e27974 100644 --- a/common/common_structs/src/wrapper_types.rs +++ b/common/common_structs/src/wrapper_types.rs @@ -15,6 +15,20 @@ impl TokenPair { } } +#[derive(TopEncode, TopDecode, NestedEncode, NestedDecode, TypeAbi)] +pub struct NonceAmountPair { + pub nonce: u64, + pub amount: BigUint, +} + +impl NonceAmountPair { + #[inline] + pub fn new(nonce: u64, amount: BigUint) -> Self { + NonceAmountPair { nonce, amount } + } +} + + #[derive( TypeAbi, TopEncode, TopDecode, NestedEncode, NestedDecode, ManagedVecItem, Clone, Debug, )] diff --git a/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs b/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs index 4571b888d..81f396e70 100644 --- a/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs +++ b/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs @@ -1,7 +1,7 @@ elrond_wasm::imports!(); elrond_wasm::derive_imports!(); -use common_structs::{Epoch, Nonce}; +use common_structs::{Epoch, Nonce, NonceAmountPair}; use simple_lock::locked_token::LockedTokenAttributes; use crate::lock_options::{EPOCHS_PER_YEAR}; @@ -28,7 +28,7 @@ pub mod fees_collector_proxy { #[payable("*")] #[endpoint(depositPenaltyFees)] fn deposit_penalty_fees(&self); - + } } #[elrond_wasm::module] @@ -127,7 +127,7 @@ pub trait UnlockWithPenaltyModule: let mut energy = self.get_updated_energy_entry_for_user(&caller); energy.deplete_after_early_unlock(&payment.amount, attributes.unlock_epoch, current_epoch); - let mut unlocked_tokens = self.unlock_tokens_unchecked(payment, &attributes); + let mut unlocked_tokens = self.unlock_tokens_unchecked(payment.clone(), &attributes); let unlocked_token_id = unlocked_tokens.token_identifier.clone().unwrap_esdt(); let new_unlock_epoch = attributes.unlock_epoch - epochs_to_reduce; @@ -146,7 +146,7 @@ pub trait UnlockWithPenaltyModule: "No tokens remaining after penalty is applied" ); - self.burn_penalty(unlocked_token_id, &payment.token_nonce, &penalty_amount); + self.burn_penalty(unlocked_token_id, payment.token_nonce, &penalty_amount); } let output_payment = self.lock_and_send(&caller, unlocked_tokens, new_unlock_epoch); @@ -214,35 +214,43 @@ pub trait UnlockWithPenaltyModule: // self.send_fees_to_collector(token_id, remaining_amount); if self.fees_from_penalty_unlocking().is_empty() { self.fees_from_penalty_unlocking() - .set(token_nonce, remaining_amount); + .set(NonceAmountPair::new(token_nonce, remaining_amount)); } else { - let (existing_nonce, existing_amount) = self.fees_from_penalty_unlocking().get(); + let existing_nonce_amount_pair = self.fees_from_penalty_unlocking().get(); let new_token_attributes: LockedTokenAttributes = locked_token_mapper.get_token_attributes(token_nonce); - let new_amount = existing_amount + remaining_amount; + let new_amount = &existing_nonce_amount_pair.amount + &remaining_amount; - locked_token_mapper.nft_burn(existing_nonce, existing_amount); - locked_token_mapper.nft_burn(token_nonce, remaining_amount); + locked_token_mapper.nft_burn( + existing_nonce_amount_pair.nonce, + &existing_nonce_amount_pair.amount, + ); + locked_token_mapper.nft_burn(token_nonce, &remaining_amount); let new_token_payment = - locked_token_mapper.nft_create(new_amount, new_token_attributes); - self.fees_from_penalty_unlocking() - .set(new_token_payment.token_nonce, new_token_payment.amount); + locked_token_mapper.nft_create(new_amount, &new_token_attributes); + self.fees_from_penalty_unlocking().set(NonceAmountPair::new( + new_token_payment.token_nonce, + new_token_payment.amount, + )); } } } #[payable("*")] #[endpoint(sendFeesToCollector)] - fn send_fees_to_collector(&self, token_id: TokenIdentifier, amount: BigUint) { + fn send_fees_to_collector(&self) { let sc_address = self.fees_collector_address().get(); let locked_token_id = self.locked_token().get_token_id(); - let (nonce, amount) = self.fees_from_penalty_unlocking().get(); + let nonce_amount_pair = self.fees_from_penalty_unlocking().get(); - let fees_amount = self - .fees_collector_proxy_builder(sc_address) + self.fees_collector_proxy_builder(sc_address) .deposit_penalty_fees() - .add_esdt_token_transfer(locked_token_id, nonce, amount) + .add_esdt_token_transfer( + locked_token_id, + nonce_amount_pair.nonce, + nonce_amount_pair.amount, + ) .execute_on_dest_context_ignore_result(); } @@ -266,5 +274,5 @@ pub trait UnlockWithPenaltyModule: #[view(getFeesFromPenaltyUnlocking)] #[storage_mapper("fees_from_penalty_unlocking")] - fn fees_from_penalty_unlocking(&self) -> SingleValueMapper; + fn fees_from_penalty_unlocking(&self) -> SingleValueMapper>; } From d97f7a0618e9cb98e824fe0e4f8d5e2251268fe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Tue, 18 Oct 2022 11:06:04 +0300 Subject: [PATCH 03/20] Burn tokens from storage_mapper once sent to FC --- common/common_structs/src/wrapper_types.rs | 3 +-- .../simple-lock-energy/src/unlock_with_penalty.rs | 4 ++++ .../simple-lock-energy/tests/simple_lock_energy_test.rs | 8 +++++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/common/common_structs/src/wrapper_types.rs b/common/common_structs/src/wrapper_types.rs index 7f3e27974..1090f0942 100644 --- a/common/common_structs/src/wrapper_types.rs +++ b/common/common_structs/src/wrapper_types.rs @@ -26,8 +26,7 @@ impl NonceAmountPair { pub fn new(nonce: u64, amount: BigUint) -> Self { NonceAmountPair { nonce, amount } } -} - +} #[derive( TypeAbi, TopEncode, TopDecode, NestedEncode, NestedDecode, ManagedVecItem, Clone, Debug, diff --git a/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs b/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs index 81f396e70..c16b3b46a 100644 --- a/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs +++ b/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs @@ -243,6 +243,10 @@ pub trait UnlockWithPenaltyModule: let sc_address = self.fees_collector_address().get(); let locked_token_id = self.locked_token().get_token_id(); let nonce_amount_pair = self.fees_from_penalty_unlocking().get(); + let locked_token_mapper = self.locked_token(); + + locked_token_mapper.nft_burn(nonce_amount_pair.nonce, &nonce_amount_pair.amount); + self.fees_from_penalty_unlocking().clear(); self.fees_collector_proxy_builder(sc_address) .deposit_penalty_fees() diff --git a/locked-asset/simple-lock-energy/tests/simple_lock_energy_test.rs b/locked-asset/simple-lock-energy/tests/simple_lock_energy_test.rs index 5c2c6f4b0..b8f30500f 100644 --- a/locked-asset/simple-lock-energy/tests/simple_lock_energy_test.rs +++ b/locked-asset/simple-lock-energy/tests/simple_lock_energy_test.rs @@ -220,16 +220,18 @@ fn reduce_lock_period_test() { }), ); - // check the tokens were half burned, half set to fees collector + //check the tokens were half burned, half saved into the Energy contract setup.b_mock.check_esdt_balance( &setup.sc_wrapper.address_ref(), BASE_ASSET_TOKEN_ID, - &rust_biguint!(0), + &(penalty_amount / 2u64), ); + + //at this point, the fee collector should not receive any tokens setup.b_mock.check_esdt_balance( &setup.fees_collector_mock, BASE_ASSET_TOKEN_ID, - &(penalty_amount / 2u64), + &rust_biguint!(0), ); // check new energy amount From 87472f00165c4bc394bf5b9e173fe4b10d219183 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Tue, 18 Oct 2022 11:20:33 +0300 Subject: [PATCH 04/20] Modify FC to accept any ESDT payment --- .../fees-collector/src/fees_accumulation.rs | 15 ++++++++++----- .../src/unlock_with_penalty.rs | 6 +----- .../tests/simple_lock_energy_setup/mod.rs | 18 ++++++++++++++++++ 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/energy-integration/fees-collector/src/fees_accumulation.rs b/energy-integration/fees-collector/src/fees_accumulation.rs index 48794e829..4a709e63f 100644 --- a/energy-integration/fees-collector/src/fees_accumulation.rs +++ b/energy-integration/fees-collector/src/fees_accumulation.rs @@ -21,17 +21,22 @@ pub trait FeesAccumulationModule: "Only known contracts can deposit" ); - let (payment_token, payment_amount) = self.call_value().single_fungible_esdt(); + let payment = self.call_value().single_esdt(); require!( - self.known_tokens().contains(&payment_token), + self.known_tokens().contains(&payment.token_identifier), "Invalid payment token" ); let current_week = self.get_current_week(); - self.accumulated_fees(current_week, &payment_token) - .update(|amt| *amt += &payment_amount); + self.accumulated_fees(current_week, &payment.token_identifier) + .update(|amt| *amt += &payment.amount); - self.emit_deposit_swap_fees_event(caller, current_week, payment_token, payment_amount); + self.emit_deposit_swap_fees_event( + caller, + current_week, + payment.token_identifier, + payment.amount, + ); } fn collect_accumulated_fees_for_week( diff --git a/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs b/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs index c16b3b46a..45fa24a81 100644 --- a/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs +++ b/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs @@ -24,10 +24,6 @@ pub mod fees_collector_proxy { #[payable("*")] #[endpoint(depositSwapFees)] fn deposit_swap_fees(&self); - - #[payable("*")] - #[endpoint(depositPenaltyFees)] - fn deposit_penalty_fees(&self); } } @@ -249,7 +245,7 @@ pub trait UnlockWithPenaltyModule: self.fees_from_penalty_unlocking().clear(); self.fees_collector_proxy_builder(sc_address) - .deposit_penalty_fees() + .deposit_swap_fees() .add_esdt_token_transfer( locked_token_id, nonce_amount_pair.nonce, diff --git a/locked-asset/simple-lock-energy/tests/simple_lock_energy_setup/mod.rs b/locked-asset/simple-lock-energy/tests/simple_lock_energy_setup/mod.rs index 581372549..21e7d654a 100644 --- a/locked-asset/simple-lock-energy/tests/simple_lock_energy_setup/mod.rs +++ b/locked-asset/simple-lock-energy/tests/simple_lock_energy_setup/mod.rs @@ -222,6 +222,24 @@ where ) } + pub fn send_fees_to_collector( + &mut self, + caller: &Address, + token_nonce: u64, + amount: u64, + ) -> TxResult { + self.b_mock.execute_esdt_transfer( + caller, + &self.sc_wrapper, + LOCKED_TOKEN_ID, + token_nonce, + &rust_biguint!(amount), + |sc| { + sc.send_fees_to_collector(); + }, + ) + } + pub fn get_penalty_amount( &mut self, token_amount: u64, From 851a8b08f5f64f127e95419c84b10ac0054f4ae4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Tue, 18 Oct 2022 17:45:16 +0300 Subject: [PATCH 05/20] Fixes after review --- common/common_structs/src/alias_types.rs | 1 + .../weekly-rewards-splitting/src/lib.rs | 6 +- .../fees-collector/src/fees_accumulation.rs | 43 +++++++-- .../src/unlock_with_penalty.rs | 93 ++++++++++++------- 4 files changed, 102 insertions(+), 41 deletions(-) diff --git a/common/common_structs/src/alias_types.rs b/common/common_structs/src/alias_types.rs index b619eded1..750c00fc2 100644 --- a/common/common_structs/src/alias_types.rs +++ b/common/common_structs/src/alias_types.rs @@ -4,6 +4,7 @@ use crate::{LockedAssetTokenAttributesEx, UnlockSchedule}; pub type Nonce = u64; pub type Epoch = u64; +pub type Week = u64; pub type PaymentsVec = ManagedVec>; pub type UnlockPeriod = UnlockSchedule; pub type OldLockedTokenAttributes = LockedAssetTokenAttributesEx; diff --git a/energy-integration/common-modules/weekly-rewards-splitting/src/lib.rs b/energy-integration/common-modules/weekly-rewards-splitting/src/lib.rs index dfd8f521e..11b0e141a 100644 --- a/energy-integration/common-modules/weekly-rewards-splitting/src/lib.rs +++ b/energy-integration/common-modules/weekly-rewards-splitting/src/lib.rs @@ -11,7 +11,7 @@ pub mod events; pub mod global_info; use base_impl::WeeklyRewardsSplittingTraitsModule; -use common_types::{PaymentsVec, TokenAmountPairsVec}; +use common_types::{Nonce, PaymentsVec}; use energy_query::Energy; use week_timekeeping::{Week, EPOCHS_IN_WEEK}; @@ -89,7 +89,6 @@ pub trait WeeklyRewardsSplittingModule: claim_progress.week = current_week; claim_progress.energy = current_user_energy; } - claim_progress_mapper.set(&claim_progress); self.emit_claim_multi_event( @@ -149,7 +148,7 @@ pub trait WeeklyRewardsSplittingModule: for weekly_reward in total_rewards { let reward_amount = weekly_reward.amount * energy_amount / &total_energy; if reward_amount > 0 { - user_rewards.push(EsdtTokenPayment::new(weekly_reward.token, 0, reward_amount)); + user_rewards.push(EsdtTokenPayment::new(weekly_reward.token_identifier, 0, reward_amount)); } } @@ -201,6 +200,7 @@ pub trait WeeklyRewardsSplittingModule: fn current_claim_progress( &self, user: &ManagedAddress, + nonce: Nonce, ) -> SingleValueMapper>; #[view(getUserEnergyForWeek)] diff --git a/energy-integration/fees-collector/src/fees_accumulation.rs b/energy-integration/fees-collector/src/fees_accumulation.rs index 4a709e63f..dbdf52b24 100644 --- a/energy-integration/fees-collector/src/fees_accumulation.rs +++ b/energy-integration/fees-collector/src/fees_accumulation.rs @@ -1,7 +1,7 @@ elrond_wasm::imports!(); elrond_wasm::derive_imports!(); -use common_types::TokenAmountPair; + use week_timekeeping::Week; #[elrond_wasm::module] @@ -28,8 +28,13 @@ pub trait FeesAccumulationModule: ); let current_week = self.get_current_week(); - self.accumulated_fees(current_week, &payment.token_identifier) - .update(|amt| *amt += &payment.amount); + if payment.token_nonce == 0 { + self.accumulated_fees(current_week, &payment.token_identifier) + .update(|amt| *amt += &payment.amount); + } else { + self.accumulated_locked_fees(current_week, &payment.token_identifier) + .update(|locked_fees| locked_fees.push(payment.clone())); + } self.emit_deposit_swap_fees_event( caller, @@ -42,16 +47,20 @@ pub trait FeesAccumulationModule: fn collect_accumulated_fees_for_week( &self, week: Week, - ) -> ManagedVec> { + ) -> ManagedVec> { let mut results = ManagedVec::new(); let all_tokens = self.all_tokens().get(); for token in &all_tokens { let opt_accumulated_fees = self.get_and_clear_acccumulated_fees(week, &token); if let Some(accumulated_fees) = opt_accumulated_fees { - results.push(TokenAmountPair::new(token, accumulated_fees)); + results.push(EsdtTokenPayment::new(token, 0, accumulated_fees)); } - } + let opt_accumulated_locked_fees = self.get_and_clear_acccumulated_locked_fees(week, &token); + if let Some(accumulated_locked_fees) = opt_accumulated_locked_fees { + results.append_vec(accumulated_locked_fees); + } + } results } @@ -64,7 +73,21 @@ pub trait FeesAccumulationModule: let value = mapper.get(); if value > 0 { mapper.clear(); + Some(value) + } else { + None + } + } + fn get_and_clear_acccumulated_locked_fees( + &self, + week: Week, + token: &TokenIdentifier, + ) -> Option>> { + let mapper = self.accumulated_locked_fees(week, token); + let value = mapper.get(); + if !value.is_empty() { + mapper.clear(); Some(value) } else { None @@ -74,4 +97,12 @@ pub trait FeesAccumulationModule: #[view(getAccumulatedFees)] #[storage_mapper("accumulatedFees")] fn accumulated_fees(&self, week: Week, token: &TokenIdentifier) -> SingleValueMapper; + + #[view(getAccumulatedLockedFees)] + #[storage_mapper("accumulatedLockedFees")] + fn accumulated_locked_fees( + &self, + week: Week, + token: &TokenIdentifier, + ) -> SingleValueMapper>>; } diff --git a/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs b/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs index 45fa24a81..b9c0ba0fd 100644 --- a/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs +++ b/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs @@ -1,13 +1,15 @@ elrond_wasm::imports!(); elrond_wasm::derive_imports!(); -use common_structs::{Epoch, Nonce, NonceAmountPair}; +use common_structs::{Epoch, Nonce, NonceAmountPair, Week}; +use mergeable::Mergeable; use simple_lock::locked_token::LockedTokenAttributes; -use crate::lock_options::{EPOCHS_PER_YEAR}; +use crate::{lock_options::EPOCHS_PER_MONTH, token_merging::LockedAmountAttributesPair}; const MAX_PERCENTAGE: u16 = 10_000; // 100% const MIN_EPOCHS_TO_REDUCE: Epoch = 1; +const EPOCHS_PER_WEEK: Epoch = 7; static INVALID_PERCENTAGE_ERR_MSG: &[u8] = b"Invalid percentage value"; #[derive(TypeAbi, TopEncode, TopDecode)] @@ -111,12 +113,12 @@ pub trait UnlockWithPenaltyModule: let attributes: LockedTokenAttributes = locked_token_mapper.get_token_attributes(payment.token_nonce); - locked_token_mapper.nft_burn(payment.token_nonce, &payment.amount); - let epochs_to_reduce = self.resolve_opt_epochs_to_reduce(opt_epochs_to_reduce, attributes.unlock_epoch); let penalty_amount = self.calculate_penalty_amount(&payment.amount, epochs_to_reduce); + locked_token_mapper.nft_burn(payment.token_nonce, &(&payment.amount - &penalty_amount)); + let current_epoch = self.blockchain().get_block_epoch(); let caller = self.blockchain().get_caller(); @@ -127,13 +129,10 @@ pub trait UnlockWithPenaltyModule: let unlocked_token_id = unlocked_tokens.token_identifier.clone().unwrap_esdt(); let new_unlock_epoch = attributes.unlock_epoch - epochs_to_reduce; - let amount_to_mint = if new_unlock_epoch == current_epoch { - &unlocked_tokens.amount - } else { - &penalty_amount - }; - self.send() - .esdt_local_mint(&unlocked_token_id, 0, amount_to_mint); + if new_unlock_epoch == current_epoch { + self.send() + .esdt_local_mint(&unlocked_token_id, 0, &unlocked_tokens.amount); + } if penalty_amount > 0 { unlocked_tokens.amount -= &penalty_amount; @@ -142,7 +141,7 @@ pub trait UnlockWithPenaltyModule: "No tokens remaining after penalty is applied" ); - self.burn_penalty(unlocked_token_id, payment.token_nonce, &penalty_amount); + self.burn_penalty(locked_token_mapper.get_token_id(), payment.token_nonce, &penalty_amount); } let output_payment = self.lock_and_send(&caller, unlocked_tokens, new_unlock_epoch); @@ -201,38 +200,63 @@ pub trait UnlockWithPenaltyModule: let fees_burn_percentage = self.fees_burn_percentage().get(); let burn_amount = fees_amount * fees_burn_percentage as u64 / MAX_PERCENTAGE as u64; let remaining_amount = fees_amount - &burn_amount; - let locked_token_mapper = self.locked_token(); if burn_amount > 0 { self.send().esdt_local_burn(&token_id, 0, &burn_amount); } if remaining_amount > 0 { - // self.send_fees_to_collector(token_id, remaining_amount); if self.fees_from_penalty_unlocking().is_empty() { + // First fee deposit of the week self.fees_from_penalty_unlocking() .set(NonceAmountPair::new(token_nonce, remaining_amount)); } else { - let existing_nonce_amount_pair = self.fees_from_penalty_unlocking().get(); - let new_token_attributes: LockedTokenAttributes = - locked_token_mapper.get_token_attributes(token_nonce); - let new_amount = &existing_nonce_amount_pair.amount + &remaining_amount; - - locked_token_mapper.nft_burn( - existing_nonce_amount_pair.nonce, - &existing_nonce_amount_pair.amount, - ); - locked_token_mapper.nft_burn(token_nonce, &remaining_amount); - - let new_token_payment = - locked_token_mapper.nft_create(new_amount, &new_token_attributes); - self.fees_from_penalty_unlocking().set(NonceAmountPair::new( - new_token_payment.token_nonce, - new_token_payment.amount, - )); + self.merge_fees_from_penalty(token_nonce, &remaining_amount) } } + + // Send fees to FeeCollector SC + let current_epoch = self.blockchain().get_block_epoch(); + let last_week_fee_sent_to_collector = self.last_week_fee_sent_to_collector().get(); + if current_epoch >= last_week_fee_sent_to_collector + EPOCHS_PER_WEEK { + self.send_fees_to_collector(); + } + } + + /// Merges new fees with existing fees and saves in storage + fn merge_fees_from_penalty(&self, token_nonce: Nonce, new_fee_amount: &BigUint) { + let locked_token_mapper = self.locked_token(); + let existing_nonce_amount_pair = self.fees_from_penalty_unlocking().get(); + let existing_token_attributes: LockedTokenAttributes = + locked_token_mapper.get_token_attributes(existing_nonce_amount_pair.nonce); + let mut output_pair = LockedAmountAttributesPair { + token_amount: existing_nonce_amount_pair.amount.clone(), + attributes: existing_token_attributes, + }; + + locked_token_mapper.nft_burn( + existing_nonce_amount_pair.nonce, + &existing_nonce_amount_pair.amount, + ); + + let new_token_attributes: LockedTokenAttributes = + locked_token_mapper.get_token_attributes(token_nonce); + let new_pair = LockedAmountAttributesPair { + token_amount: new_fee_amount.clone(), + attributes: new_token_attributes, + }; + + locked_token_mapper.nft_burn(token_nonce, &new_fee_amount); + + + output_pair.merge_with(new_pair); + + self.fees_from_penalty_unlocking().set(NonceAmountPair::new( + output_pair.attributes.original_token_nonce, + output_pair.token_amount, + )); } + #[payable("*")] #[endpoint(sendFeesToCollector)] fn send_fees_to_collector(&self) { @@ -273,6 +297,11 @@ pub trait UnlockWithPenaltyModule: fn fees_collector_address(&self) -> SingleValueMapper; #[view(getFeesFromPenaltyUnlocking)] - #[storage_mapper("fees_from_penalty_unlocking")] + #[storage_mapper("feesFromPenaltyUnlocking")] fn fees_from_penalty_unlocking(&self) -> SingleValueMapper>; + + #[view(getLastWeekFeeSentToCollector)] + #[storage_mapper("lastWeekFeeSentToCollector")] + fn last_week_fee_sent_to_collector(&self) -> SingleValueMapper; + } From 6e3b2b0b908434a9ce4e8e4ca87617e06323cc41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 19 Oct 2022 09:26:03 +0300 Subject: [PATCH 06/20] Fixed compiling issues --- .../common-modules/weekly-rewards-splitting/src/lib.rs | 6 +++--- energy-integration/fees-collector/src/fees_accumulation.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/energy-integration/common-modules/weekly-rewards-splitting/src/lib.rs b/energy-integration/common-modules/weekly-rewards-splitting/src/lib.rs index 11b0e141a..51e7a7174 100644 --- a/energy-integration/common-modules/weekly-rewards-splitting/src/lib.rs +++ b/energy-integration/common-modules/weekly-rewards-splitting/src/lib.rs @@ -89,6 +89,7 @@ pub trait WeeklyRewardsSplittingModule: claim_progress.week = current_week; claim_progress.energy = current_user_energy; } + claim_progress_mapper.set(&claim_progress); self.emit_claim_multi_event( @@ -140,13 +141,13 @@ pub trait WeeklyRewardsSplittingModule: total_rewards: &TokenAmountPairsVec, ) -> PaymentsVec { let mut user_rewards = ManagedVec::new(); - if energy_amount == &0 { + if energy_amount == 0 { return user_rewards; } let total_energy = self.total_energy_for_week(week).get(); for weekly_reward in total_rewards { - let reward_amount = weekly_reward.amount * energy_amount / &total_energy; + let reward_amount = weekly_reward.amount * &energy_amount / &total_energy; if reward_amount > 0 { user_rewards.push(EsdtTokenPayment::new(weekly_reward.token_identifier, 0, reward_amount)); } @@ -200,7 +201,6 @@ pub trait WeeklyRewardsSplittingModule: fn current_claim_progress( &self, user: &ManagedAddress, - nonce: Nonce, ) -> SingleValueMapper>; #[view(getUserEnergyForWeek)] diff --git a/energy-integration/fees-collector/src/fees_accumulation.rs b/energy-integration/fees-collector/src/fees_accumulation.rs index dbdf52b24..7c9991b96 100644 --- a/energy-integration/fees-collector/src/fees_accumulation.rs +++ b/energy-integration/fees-collector/src/fees_accumulation.rs @@ -53,7 +53,7 @@ pub trait FeesAccumulationModule: for token in &all_tokens { let opt_accumulated_fees = self.get_and_clear_acccumulated_fees(week, &token); if let Some(accumulated_fees) = opt_accumulated_fees { - results.push(EsdtTokenPayment::new(token, 0, accumulated_fees)); + results.push(EsdtTokenPayment::new(token.clone(), 0, accumulated_fees)); } let opt_accumulated_locked_fees = self.get_and_clear_acccumulated_locked_fees(week, &token); From 939f7821f8d3af8eef98debb9e6ded901426533c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 19 Oct 2022 13:33:30 +0300 Subject: [PATCH 07/20] Fix existing tests and create 2 new tests --- .../weekly-rewards-splitting/src/lib.rs | 6 +- .../fees-collector/src/fees_accumulation.rs | 4 +- .../tests/fees_collector_rust_test.rs | 58 ++--- .../src/unlock_with_penalty.rs | 41 ++-- .../tests/simple_lock_energy_setup/mod.rs | 1 + .../tests/simple_lock_energy_test.rs | 217 +++++++++++++++++- 6 files changed, 279 insertions(+), 48 deletions(-) diff --git a/energy-integration/common-modules/weekly-rewards-splitting/src/lib.rs b/energy-integration/common-modules/weekly-rewards-splitting/src/lib.rs index 51e7a7174..27d8a3660 100644 --- a/energy-integration/common-modules/weekly-rewards-splitting/src/lib.rs +++ b/energy-integration/common-modules/weekly-rewards-splitting/src/lib.rs @@ -149,7 +149,11 @@ pub trait WeeklyRewardsSplittingModule: for weekly_reward in total_rewards { let reward_amount = weekly_reward.amount * &energy_amount / &total_energy; if reward_amount > 0 { - user_rewards.push(EsdtTokenPayment::new(weekly_reward.token_identifier, 0, reward_amount)); + user_rewards.push(EsdtTokenPayment::new( + weekly_reward.token_identifier, + 0, + reward_amount, + )); } } diff --git a/energy-integration/fees-collector/src/fees_accumulation.rs b/energy-integration/fees-collector/src/fees_accumulation.rs index 7c9991b96..8032adba4 100644 --- a/energy-integration/fees-collector/src/fees_accumulation.rs +++ b/energy-integration/fees-collector/src/fees_accumulation.rs @@ -1,7 +1,6 @@ elrond_wasm::imports!(); elrond_wasm::derive_imports!(); - use week_timekeeping::Week; #[elrond_wasm::module] @@ -56,7 +55,8 @@ pub trait FeesAccumulationModule: results.push(EsdtTokenPayment::new(token.clone(), 0, accumulated_fees)); } - let opt_accumulated_locked_fees = self.get_and_clear_acccumulated_locked_fees(week, &token); + let opt_accumulated_locked_fees = + self.get_and_clear_acccumulated_locked_fees(week, &token); if let Some(accumulated_locked_fees) = opt_accumulated_locked_fees { results.append_vec(accumulated_locked_fees); } diff --git a/energy-integration/fees-collector/tests/fees_collector_rust_test.rs b/energy-integration/fees-collector/tests/fees_collector_rust_test.rs index 37255b506..64a02c94c 100644 --- a/energy-integration/fees-collector/tests/fees_collector_rust_test.rs +++ b/energy-integration/fees-collector/tests/fees_collector_rust_test.rs @@ -1,9 +1,8 @@ mod fees_collector_test_setup; -use common_types::TokenAmountPair; use elrond_wasm::{ elrond_codec::multi_types::OptionalValue, - types::{BigInt, ManagedVec, MultiValueEncoded, OperationCompletionStatus}, + types::{BigInt, EsdtTokenPayment, ManagedVec, MultiValueEncoded, OperationCompletionStatus}, }; use elrond_wasm_debug::{managed_address, managed_biguint, managed_token_id, rust_biguint}; use elrond_wasm_modules::pause::PauseModule; @@ -321,15 +320,16 @@ fn claim_second_week_test() { .b_mock .execute_query(&fc_setup.fc_wrapper, |sc| { let mut expected_total_rewards = ManagedVec::new(); - expected_total_rewards.push(TokenAmountPair { - token: managed_token_id!(FIRST_TOKEN_ID), - amount: managed_biguint!(USER_BALANCE), - }); - expected_total_rewards.push(TokenAmountPair { - token: managed_token_id!(SECOND_TOKEN_ID), - amount: managed_biguint!(USER_BALANCE / 2), - }); - + expected_total_rewards.push(EsdtTokenPayment::new( + managed_token_id!(FIRST_TOKEN_ID), + 0, + managed_biguint!(USER_BALANCE), + )); + expected_total_rewards.push(EsdtTokenPayment::new( + managed_token_id!(SECOND_TOKEN_ID), + 0, + managed_biguint!(USER_BALANCE / 2), + )); assert_eq!(expected_total_rewards, sc.total_rewards_for_week(1).get()); }) .assert_ok(); @@ -366,14 +366,16 @@ fn claim_second_week_test() { ); let mut expected_total_rewards = ManagedVec::new(); - expected_total_rewards.push(TokenAmountPair { - token: managed_token_id!(FIRST_TOKEN_ID), - amount: managed_biguint!(USER_BALANCE), - }); - expected_total_rewards.push(TokenAmountPair { - token: managed_token_id!(SECOND_TOKEN_ID), - amount: managed_biguint!(USER_BALANCE / 2), - }); + expected_total_rewards.push(EsdtTokenPayment::new( + managed_token_id!(FIRST_TOKEN_ID), + 0, + managed_biguint!(USER_BALANCE), + )); + expected_total_rewards.push(EsdtTokenPayment::new( + managed_token_id!(SECOND_TOKEN_ID), + 0, + managed_biguint!(USER_BALANCE / 2), + )); assert_eq!(sc.total_rewards_for_week(1).get(), expected_total_rewards); // first user's new energy is added to week 2 @@ -426,14 +428,16 @@ fn claim_second_week_test() { .b_mock .execute_query(&fc_setup.fc_wrapper, |sc| { let mut expected_total_rewards = ManagedVec::new(); - expected_total_rewards.push(TokenAmountPair { - token: managed_token_id!(FIRST_TOKEN_ID), - amount: managed_biguint!(USER_BALANCE), - }); - expected_total_rewards.push(TokenAmountPair { - token: managed_token_id!(SECOND_TOKEN_ID), - amount: managed_biguint!(USER_BALANCE / 2), - }); + expected_total_rewards.push(EsdtTokenPayment::new( + managed_token_id!(FIRST_TOKEN_ID), + 0, + managed_biguint!(USER_BALANCE), + )); + expected_total_rewards.push(EsdtTokenPayment::new( + managed_token_id!(SECOND_TOKEN_ID), + 0, + managed_biguint!(USER_BALANCE / 2), + )); assert_eq!(sc.total_rewards_for_week(1).get(), expected_total_rewards); // first user's new energy is added to week 2 diff --git a/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs b/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs index b9c0ba0fd..897778611 100644 --- a/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs +++ b/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs @@ -130,8 +130,11 @@ pub trait UnlockWithPenaltyModule: let new_unlock_epoch = attributes.unlock_epoch - epochs_to_reduce; if new_unlock_epoch == current_epoch { - self.send() - .esdt_local_mint(&unlocked_token_id, 0, &unlocked_tokens.amount); + self.send().esdt_local_mint( + &unlocked_token_id, + 0, + &(&unlocked_tokens.amount - &penalty_amount), + ); } if penalty_amount > 0 { @@ -141,7 +144,11 @@ pub trait UnlockWithPenaltyModule: "No tokens remaining after penalty is applied" ); - self.burn_penalty(locked_token_mapper.get_token_id(), payment.token_nonce, &penalty_amount); + self.burn_penalty( + locked_token_mapper.get_token_id(), + payment.token_nonce, + &penalty_amount, + ); } let output_payment = self.lock_and_send(&caller, unlocked_tokens, new_unlock_epoch); @@ -202,7 +209,8 @@ pub trait UnlockWithPenaltyModule: let remaining_amount = fees_amount - &burn_amount; if burn_amount > 0 { - self.send().esdt_local_burn(&token_id, 0, &burn_amount); + self.send() + .esdt_local_burn(&token_id, token_nonce, &burn_amount); } if remaining_amount > 0 { if self.fees_from_penalty_unlocking().is_empty() { @@ -219,6 +227,7 @@ pub trait UnlockWithPenaltyModule: let last_week_fee_sent_to_collector = self.last_week_fee_sent_to_collector().get(); if current_epoch >= last_week_fee_sent_to_collector + EPOCHS_PER_WEEK { self.send_fees_to_collector(); + self.last_week_fee_sent_to_collector().set(current_epoch); } } @@ -227,7 +236,8 @@ pub trait UnlockWithPenaltyModule: let locked_token_mapper = self.locked_token(); let existing_nonce_amount_pair = self.fees_from_penalty_unlocking().get(); let existing_token_attributes: LockedTokenAttributes = - locked_token_mapper.get_token_attributes(existing_nonce_amount_pair.nonce); + locked_token_mapper.get_token_attributes(existing_nonce_amount_pair.nonce); + let mut output_pair = LockedAmountAttributesPair { token_amount: existing_nonce_amount_pair.amount.clone(), attributes: existing_token_attributes, @@ -239,7 +249,7 @@ pub trait UnlockWithPenaltyModule: ); let new_token_attributes: LockedTokenAttributes = - locked_token_mapper.get_token_attributes(token_nonce); + locked_token_mapper.get_token_attributes(token_nonce); let new_pair = LockedAmountAttributesPair { token_amount: new_fee_amount.clone(), attributes: new_token_attributes, @@ -247,27 +257,31 @@ pub trait UnlockWithPenaltyModule: locked_token_mapper.nft_burn(token_nonce, &new_fee_amount); - output_pair.merge_with(new_pair); + let sft_nonce = self.get_or_create_nonce_for_attributes( + &locked_token_mapper, + &output_pair.attributes.original_token_id.clone().into_name(), + &output_pair.attributes, + ); + + let new_locked_tokens = + locked_token_mapper.nft_add_quantity(sft_nonce, output_pair.token_amount.clone()); + self.fees_from_penalty_unlocking().set(NonceAmountPair::new( - output_pair.attributes.original_token_nonce, - output_pair.token_amount, + new_locked_tokens.token_nonce, + new_locked_tokens.amount, )); } - #[payable("*")] #[endpoint(sendFeesToCollector)] fn send_fees_to_collector(&self) { let sc_address = self.fees_collector_address().get(); let locked_token_id = self.locked_token().get_token_id(); let nonce_amount_pair = self.fees_from_penalty_unlocking().get(); - let locked_token_mapper = self.locked_token(); - locked_token_mapper.nft_burn(nonce_amount_pair.nonce, &nonce_amount_pair.amount); self.fees_from_penalty_unlocking().clear(); - self.fees_collector_proxy_builder(sc_address) .deposit_swap_fees() .add_esdt_token_transfer( @@ -303,5 +317,4 @@ pub trait UnlockWithPenaltyModule: #[view(getLastWeekFeeSentToCollector)] #[storage_mapper("lastWeekFeeSentToCollector")] fn last_week_fee_sent_to_collector(&self) -> SingleValueMapper; - } diff --git a/locked-asset/simple-lock-energy/tests/simple_lock_energy_setup/mod.rs b/locked-asset/simple-lock-energy/tests/simple_lock_energy_setup/mod.rs index 21e7d654a..5b73bc234 100644 --- a/locked-asset/simple-lock-energy/tests/simple_lock_energy_setup/mod.rs +++ b/locked-asset/simple-lock-energy/tests/simple_lock_energy_setup/mod.rs @@ -20,6 +20,7 @@ mod fees_collector_mock; use fees_collector_mock::*; pub const EPOCHS_IN_YEAR: u64 = 360; +pub const EPOCHS_IN_WEEK: u64 = 7; pub const USER_BALANCE: u64 = 1_000_000_000_000_000_000; pub static BASE_ASSET_TOKEN_ID: &[u8] = b"MEX-123456"; diff --git a/locked-asset/simple-lock-energy/tests/simple_lock_energy_test.rs b/locked-asset/simple-lock-energy/tests/simple_lock_energy_test.rs index b8f30500f..860529d2c 100644 --- a/locked-asset/simple-lock-energy/tests/simple_lock_energy_test.rs +++ b/locked-asset/simple-lock-energy/tests/simple_lock_energy_test.rs @@ -171,6 +171,209 @@ fn unlock_early_test() { assert_eq!(actual_energy, expected_energy); } +#[test] +fn multiple_early_unlocks_same_week_test() { + let mut setup = SimpleLockEnergySetup::new(simple_lock_energy::contract_obj); + let first_user = setup.first_user.clone(); + let half_balance = USER_BALANCE / 2; + let sixth_balance = half_balance / 3; + + let mut current_epoch = 1; + setup.b_mock.set_block_epoch(current_epoch); + + setup + .lock( + &first_user, + BASE_ASSET_TOKEN_ID, + half_balance, + LOCK_OPTIONS[0], + ) + .assert_ok(); + + // unlock early after half a year - with half a year remaining + // unlock epoch = 360, so epochs remaining after half year (1 + 365 / 2 = 183) + // = 360 - 183 = 177 + let half_year_epochs = EPOCHS_IN_YEAR / 2; + current_epoch += half_year_epochs; + setup.b_mock.set_block_epoch(current_epoch); + + let mut penalty_percentage = 485u64; // 1 + 9_999 * 177 / (10 * 365) ~= 1 + 484 = 485 + let mut expected_penalty_amount = rust_biguint!(sixth_balance) * penalty_percentage / 10_000u64; + let mut penalty_amount = setup.get_penalty_amount(sixth_balance, 177); + assert_eq!(penalty_amount, expected_penalty_amount); + + // Unlock early 1/3 of the LockedTokens + setup + .unlock_early(&first_user, 1, sixth_balance) + .assert_ok(); + + let received_token_amount = rust_biguint!(sixth_balance) - penalty_amount; + let expected_balance = &received_token_amount + half_balance; + setup + .b_mock + .check_esdt_balance(&first_user, BASE_ASSET_TOKEN_ID, &expected_balance); + + // After first early unlock of the week, fees are sent to Fee Collector SC + setup.b_mock.check_nft_balance( + &setup.fees_collector_mock, + LOCKED_TOKEN_ID, + 1, + &(&expected_penalty_amount / 2u64 + 1u64), + Some(&LockedTokenAttributes:: { + original_token_id: managed_token_id_wrapped!(BASE_ASSET_TOKEN_ID), + original_token_nonce: 0, + unlock_epoch: 360, + }), + ); + + // Unlock early the another 1/3 of the LockedTokens, same week -> First Locked Tokens + setup + .unlock_early(&first_user, 1, sixth_balance) + .assert_ok(); + + penalty_percentage = 485u64; // 1 + 9_999 * 177 / (10 * 365) ~= 1 + 484 = 485 + expected_penalty_amount = rust_biguint!(sixth_balance) * penalty_percentage / 10_000u64; + penalty_amount = setup.get_penalty_amount(sixth_balance, 177); + assert_eq!(penalty_amount, expected_penalty_amount); + + let received_token_amount_2 = rust_biguint!(sixth_balance) - penalty_amount; + let expected_balance = &received_token_amount_2 + &received_token_amount + half_balance; + setup + .b_mock + .check_esdt_balance(&first_user, BASE_ASSET_TOKEN_ID, &expected_balance); + + // Energy SC stores the fee until the end of the week + // Doesn't send it to FeeCollector yet + setup.b_mock.check_nft_balance( + &setup.sc_wrapper.address_ref(), + LOCKED_TOKEN_ID, + 1, + &(expected_penalty_amount / 2u64 + 2u64), + Some(&LockedTokenAttributes:: { + original_token_id: managed_token_id_wrapped!(BASE_ASSET_TOKEN_ID), + original_token_nonce: 0, + unlock_epoch: 360, + }), + ); + + // Unlock early the last 1/3 of the LockedTokens, same week -> Locked Token Merging + setup + .unlock_early(&first_user, 1, sixth_balance) + .assert_ok(); + + penalty_percentage = 485u64; // 1 + 9_999 * 177 / (10 * 365) ~= 1 + 484 = 485 + expected_penalty_amount = rust_biguint!(sixth_balance) * penalty_percentage / 10_000u64; + penalty_amount = setup.get_penalty_amount(sixth_balance, 177); + assert_eq!(penalty_amount, expected_penalty_amount); + + let received_token_amount_3 = rust_biguint!(sixth_balance) - penalty_amount; + let expected_balance = + &received_token_amount_3 + &received_token_amount_2 + &received_token_amount + half_balance; + setup + .b_mock + .check_esdt_balance(&first_user, BASE_ASSET_TOKEN_ID, &expected_balance); + + // Energy SC stores the fee until the end of the week + // Doesn't send it to FeeCollector yet + setup.b_mock.check_nft_balance( + &setup.sc_wrapper.address_ref(), + LOCKED_TOKEN_ID, + 1, + &(expected_penalty_amount + 2u64), + Some(&LockedTokenAttributes:: { + original_token_id: managed_token_id_wrapped!(BASE_ASSET_TOKEN_ID), + original_token_nonce: 0, + unlock_epoch: 360, + }), + ); +} + +#[test] +fn multiple_early_unlocks_multiple_weeks_fee_collector_check_test() { + let mut setup = SimpleLockEnergySetup::new(simple_lock_energy::contract_obj); + let first_user = setup.first_user.clone(); + let half_balance = USER_BALANCE / 2; + let quarter_balance = half_balance / 2; + + let mut current_epoch = 1; + setup.b_mock.set_block_epoch(current_epoch); + + setup + .lock( + &first_user, + BASE_ASSET_TOKEN_ID, + half_balance, + LOCK_OPTIONS[0], + ) + .assert_ok(); + + // unlock early after half a year - with half a year remaining + // unlock epoch = 360, so epochs remaining after half year (1 + 365 / 2 = 183) + // = 360 - 183 = 177 + let half_year_epochs = EPOCHS_IN_YEAR / 2; + current_epoch += half_year_epochs; + setup.b_mock.set_block_epoch(current_epoch); + + let mut penalty_percentage = 485u64; // 1 + 9_999 * 177 / (10 * 365) ~= 1 + 484 = 485 + let expected_penalty_amount = rust_biguint!(quarter_balance) * penalty_percentage / 10_000u64; + let mut penalty_amount = setup.get_penalty_amount(quarter_balance, 177); + assert_eq!(penalty_amount, expected_penalty_amount); + + // Unlock early half of the LockedTokens + setup + .unlock_early(&first_user, 1, quarter_balance) + .assert_ok(); + + let received_token_amount = rust_biguint!(quarter_balance) - penalty_amount; + let expected_balance = &received_token_amount + half_balance; + setup + .b_mock + .check_esdt_balance(&first_user, BASE_ASSET_TOKEN_ID, &expected_balance); + + setup.b_mock.check_nft_balance( + &setup.fees_collector_mock, + LOCKED_TOKEN_ID, + 1, + &(&expected_penalty_amount / 2u64), + Some(&LockedTokenAttributes:: { + original_token_id: managed_token_id_wrapped!(BASE_ASSET_TOKEN_ID), + original_token_nonce: 0, + unlock_epoch: 360, + }), + ); + + current_epoch += EPOCHS_IN_WEEK; + setup.b_mock.set_block_epoch(current_epoch); + + // Unlock early the other half of the LockedTokens + setup + .unlock_early(&first_user, 1, quarter_balance) + .assert_ok(); + + penalty_percentage = 466u64; // 1 + 9_999 * 170 / (10 * 365) ~= 1 + 465 = 466 + let expected_penalty_amount_2 = rust_biguint!(quarter_balance) * penalty_percentage / 10_000u64; + penalty_amount = setup.get_penalty_amount(quarter_balance, 170); + assert_eq!(penalty_amount, expected_penalty_amount_2); + + let received_token_amount_2 = rust_biguint!(quarter_balance) - penalty_amount; + let expected_balance = &received_token_amount_2 + &received_token_amount + half_balance; + setup + .b_mock + .check_esdt_balance(&first_user, BASE_ASSET_TOKEN_ID, &expected_balance); + + setup.b_mock.check_nft_balance( + &setup.fees_collector_mock, + LOCKED_TOKEN_ID, + 1, + &((&expected_penalty_amount + &expected_penalty_amount_2) / 2u64), + Some(&LockedTokenAttributes:: { + original_token_id: managed_token_id_wrapped!(BASE_ASSET_TOKEN_ID), + original_token_nonce: 0, + unlock_epoch: 360, + }), + ); +} + #[test] fn reduce_lock_period_test() { let mut setup = SimpleLockEnergySetup::new(simple_lock_energy::contract_obj); @@ -220,11 +423,17 @@ fn reduce_lock_period_test() { }), ); - //check the tokens were half burned, half saved into the Energy contract - setup.b_mock.check_esdt_balance( + // Energy SC stores the fee until the end of the week + setup.b_mock.check_nft_balance( &setup.sc_wrapper.address_ref(), - BASE_ASSET_TOKEN_ID, - &(penalty_amount / 2u64), + LOCKED_TOKEN_ID, + 1, + &(penalty_amount / 2u64 + 1u64), + Some(&LockedTokenAttributes:: { + original_token_id: managed_token_id_wrapped!(BASE_ASSET_TOKEN_ID), + original_token_nonce: 0, + unlock_epoch: 360, + }), ); //at this point, the fee collector should not receive any tokens From 312fb838d1373a659295a2ce4ee90793b3035f87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 19 Oct 2022 15:47:20 +0300 Subject: [PATCH 08/20] Fix conflicts --- .../weekly-rewards-splitting/src/base_impl.rs | 6 +++--- .../common-modules/weekly-rewards-splitting/src/lib.rs | 9 +++++---- energy-integration/farm-boosted-yields/src/lib.rs | 6 +++--- energy-integration/fees-collector/src/lib.rs | 6 +++--- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/energy-integration/common-modules/weekly-rewards-splitting/src/base_impl.rs b/energy-integration/common-modules/weekly-rewards-splitting/src/base_impl.rs index 61c8c0227..74a952968 100644 --- a/energy-integration/common-modules/weekly-rewards-splitting/src/base_impl.rs +++ b/energy-integration/common-modules/weekly-rewards-splitting/src/base_impl.rs @@ -1,6 +1,6 @@ elrond_wasm::imports!(); -use common_types::TokenAmountPairsVec; +use common_types::PaymentsVec; use week_timekeeping::Week; use crate::{events, ClaimProgress}; @@ -18,7 +18,7 @@ pub trait WeeklyRewardsSplittingTraitsModule { &self, module: &Self::WeeklyRewardsSplittingMod, week: Week, - ) -> TokenAmountPairsVec<::Api> { + ) -> PaymentsVec<::Api> { let total_rewards_mapper = module.total_rewards_for_week(week); if total_rewards_mapper.is_empty() { let total_rewards = self.collect_rewards_for_week(module, week); @@ -34,7 +34,7 @@ pub trait WeeklyRewardsSplittingTraitsModule { &self, module: &Self::WeeklyRewardsSplittingMod, week: Week, - ) -> TokenAmountPairsVec<::Api>; + ) -> PaymentsVec<::Api>; fn get_claim_progress_mapper( &self, diff --git a/energy-integration/common-modules/weekly-rewards-splitting/src/lib.rs b/energy-integration/common-modules/weekly-rewards-splitting/src/lib.rs index 27d8a3660..9bc90f14c 100644 --- a/energy-integration/common-modules/weekly-rewards-splitting/src/lib.rs +++ b/energy-integration/common-modules/weekly-rewards-splitting/src/lib.rs @@ -11,7 +11,7 @@ pub mod events; pub mod global_info; use base_impl::WeeklyRewardsSplittingTraitsModule; -use common_types::{Nonce, PaymentsVec}; +use common_types::PaymentsVec; use energy_query::Energy; use week_timekeeping::{Week, EPOCHS_IN_WEEK}; @@ -138,16 +138,17 @@ pub trait WeeklyRewardsSplittingModule: &self, week: Week, energy_amount: &BigUint, - total_rewards: &TokenAmountPairsVec, + total_rewards: &PaymentsVec, ) -> PaymentsVec { + let biguint_zero = BigUint::zero(); let mut user_rewards = ManagedVec::new(); - if energy_amount == 0 { + if energy_amount == &biguint_zero { return user_rewards; } let total_energy = self.total_energy_for_week(week).get(); for weekly_reward in total_rewards { - let reward_amount = weekly_reward.amount * &energy_amount / &total_energy; + let reward_amount = weekly_reward.amount * energy_amount / &total_energy; if reward_amount > 0 { user_rewards.push(EsdtTokenPayment::new( weekly_reward.token_identifier, diff --git a/energy-integration/farm-boosted-yields/src/lib.rs b/energy-integration/farm-boosted-yields/src/lib.rs index eb460a74e..19fd68480 100644 --- a/energy-integration/farm-boosted-yields/src/lib.rs +++ b/energy-integration/farm-boosted-yields/src/lib.rs @@ -5,7 +5,7 @@ elrond_wasm::derive_imports!(); use core::marker::PhantomData; -use common_types::{Nonce, TokenAmountPair, TokenAmountPairsVec}; +use common_types::{Nonce, PaymentsVec}; use week_timekeeping::Week; use weekly_rewards_splitting::{base_impl::WeeklyRewardsSplittingTraitsModule, ClaimProgress}; @@ -121,13 +121,13 @@ where &self, module: &Self::WeeklyRewardsSplittingMod, week: Week, - ) -> TokenAmountPairsVec<::Api> { + ) -> PaymentsVec<::Api> { let reward_token_id = module.reward_token_id().get(); let rewards_mapper = module.accumulated_rewards_for_week(week); let total_rewards = rewards_mapper.get(); rewards_mapper.clear(); - ManagedVec::from_single_item(TokenAmountPair::new(reward_token_id, total_rewards)) + ManagedVec::from_single_item(EsdtTokenPayment::new(reward_token_id, 0, total_rewards)) } fn get_claim_progress_mapper( diff --git a/energy-integration/fees-collector/src/lib.rs b/energy-integration/fees-collector/src/lib.rs index 219ff4511..0d7cc99f2 100644 --- a/energy-integration/fees-collector/src/lib.rs +++ b/energy-integration/fees-collector/src/lib.rs @@ -3,7 +3,7 @@ elrond_wasm::imports!(); -use common_types::{PaymentsVec, TokenAmountPair, TokenAmountPairsVec, Week}; +use common_types::{PaymentsVec, Week}; use core::marker::PhantomData; use energy_query::Energy; use weekly_rewards_splitting::base_impl::WeeklyRewardsSplittingTraitsModule; @@ -125,13 +125,13 @@ where &self, module: &Self::WeeklyRewardsSplittingMod, week: Week, - ) -> TokenAmountPairsVec<::Api> { + ) -> PaymentsVec<::Api> { let mut results = ManagedVec::new(); let all_tokens = module.all_tokens().get(); for token in &all_tokens { let opt_accumulated_fees = module.get_and_clear_acccumulated_fees(week, &token); if let Some(accumulated_fees) = opt_accumulated_fees { - results.push(TokenAmountPair::new(token, accumulated_fees)); + results.push(EsdtTokenPayment::new(token, 0, accumulated_fees)); } } From e4651c685c44b4f9dd82916f626eccecd122a9b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 19 Oct 2022 17:16:01 +0300 Subject: [PATCH 09/20] Fixes after review --- .../simple-lock-energy/src/token_merging.rs | 49 ++++++---- .../src/unlock_with_penalty.rs | 98 +++++++++---------- .../tests/simple_lock_energy_test.rs | 4 +- .../tests/token_merging_test.rs | 2 +- 4 files changed, 83 insertions(+), 70 deletions(-) diff --git a/locked-asset/simple-lock-energy/src/token_merging.rs b/locked-asset/simple-lock-energy/src/token_merging.rs index 693ecac63..9c412b3e8 100644 --- a/locked-asset/simple-lock-energy/src/token_merging.rs +++ b/locked-asset/simple-lock-energy/src/token_merging.rs @@ -1,5 +1,6 @@ elrond_wasm::imports!(); +use common_structs::PaymentsVec; use mergeable::{weighted_average, Mergeable}; use simple_lock::locked_token::LockedTokenAttributes; @@ -49,15 +50,41 @@ pub trait TokenMergingModule: // TODO: Only allow original caller arg for whitelisted addresses #[payable("*")] #[endpoint(mergeTokens)] - fn merge_tokens(&self, opt_original_caller: OptionalValue) -> EsdtTokenPayment { + fn merge_tokens_endpoint( + &self, + opt_original_caller: OptionalValue, + ) -> EsdtTokenPayment { self.require_not_paused(); - let current_epoch = self.blockchain().get_block_epoch(); let actual_caller = self.blockchain().get_caller(); - let original_caller = self.dest_from_optional(opt_original_caller); + + let payments = self.get_non_empty_payments(); + + let output_amount_attributes = self.merge_tokens(payments, opt_original_caller); + + let simulated_lock_payment = EgldOrEsdtTokenPayment::new( + output_amount_attributes.attributes.original_token_id, + output_amount_attributes.attributes.original_token_nonce, + output_amount_attributes.token_amount, + ); + let output_tokens = self.lock_and_send( + &actual_caller, + simulated_lock_payment, + output_amount_attributes.attributes.unlock_epoch, + ); + + self.to_esdt_payment(output_tokens) + } + + fn merge_tokens( + self, + mut payments: PaymentsVec, + opt_original_caller: OptionalValue, + ) -> LockedAmountAttributesPair { let locked_token_mapper = self.locked_token(); + let original_caller = self.dest_from_optional(opt_original_caller); + let current_epoch = self.blockchain().get_block_epoch(); - let mut payments = self.get_non_empty_payments(); locked_token_mapper.require_all_same_token(&payments); let first_payment = payments.get(0); @@ -110,18 +137,6 @@ pub trait TokenMergingModule: output_pair }); - - let simulated_lock_payment = EgldOrEsdtTokenPayment::new( - output_amount_attributes.attributes.original_token_id, - output_amount_attributes.attributes.original_token_nonce, - output_amount_attributes.token_amount, - ); - let output_tokens = self.lock_and_send( - &actual_caller, - simulated_lock_payment, - output_amount_attributes.attributes.unlock_epoch, - ); - - self.to_esdt_payment(output_tokens) + output_amount_attributes } } diff --git a/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs b/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs index 897778611..92e2f0948 100644 --- a/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs +++ b/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs @@ -1,11 +1,11 @@ elrond_wasm::imports!(); elrond_wasm::derive_imports!(); -use common_structs::{Epoch, Nonce, NonceAmountPair, Week}; -use mergeable::Mergeable; +use common_structs::{Epoch, Nonce, NonceAmountPair, PaymentsVec, Week}; + use simple_lock::locked_token::LockedTokenAttributes; -use crate::{lock_options::EPOCHS_PER_MONTH, token_merging::LockedAmountAttributesPair}; +use crate::{lock_options::EPOCHS_PER_MONTH, token_merging}; const MAX_PERCENTAGE: u16 = 10_000; // 100% const MIN_EPOCHS_TO_REDUCE: Epoch = 1; @@ -39,6 +39,7 @@ pub trait UnlockWithPenaltyModule: + crate::lock_options::LockOptionsModule + crate::events::EventsModule + elrond_wasm_modules::pause::PauseModule + + token_merging::TokenMergingModule + utils::UtilsModule { /// - min_penalty_percentage / max_penalty_percentage: The penalty for early unlock @@ -218,55 +219,44 @@ pub trait UnlockWithPenaltyModule: self.fees_from_penalty_unlocking() .set(NonceAmountPair::new(token_nonce, remaining_amount)); } else { - self.merge_fees_from_penalty(token_nonce, &remaining_amount) + self.merge_fees_from_penalty(token_nonce, remaining_amount) } } - // Send fees to FeeCollector SC - let current_epoch = self.blockchain().get_block_epoch(); - let last_week_fee_sent_to_collector = self.last_week_fee_sent_to_collector().get(); - if current_epoch >= last_week_fee_sent_to_collector + EPOCHS_PER_WEEK { - self.send_fees_to_collector(); - self.last_week_fee_sent_to_collector().set(current_epoch); - } + // Only once per week + self.send_fees_to_collector(); } /// Merges new fees with existing fees and saves in storage - fn merge_fees_from_penalty(&self, token_nonce: Nonce, new_fee_amount: &BigUint) { + fn merge_fees_from_penalty(&self, token_nonce: Nonce, new_fee_amount: BigUint) { let locked_token_mapper = self.locked_token(); let existing_nonce_amount_pair = self.fees_from_penalty_unlocking().get(); - let existing_token_attributes: LockedTokenAttributes = - locked_token_mapper.get_token_attributes(existing_nonce_amount_pair.nonce); - - let mut output_pair = LockedAmountAttributesPair { - token_amount: existing_nonce_amount_pair.amount.clone(), - attributes: existing_token_attributes, - }; - - locked_token_mapper.nft_burn( + let mut payments = PaymentsVec::new(); + payments.push(EsdtTokenPayment::new( + locked_token_mapper.get_token_id(), + token_nonce, + new_fee_amount, + )); + payments.push(EsdtTokenPayment::new( + locked_token_mapper.get_token_id(), existing_nonce_amount_pair.nonce, - &existing_nonce_amount_pair.amount, - ); - - let new_token_attributes: LockedTokenAttributes = - locked_token_mapper.get_token_attributes(token_nonce); - let new_pair = LockedAmountAttributesPair { - token_amount: new_fee_amount.clone(), - attributes: new_token_attributes, - }; - - locked_token_mapper.nft_burn(token_nonce, &new_fee_amount); + existing_nonce_amount_pair.amount, + )); - output_pair.merge_with(new_pair); + let new_locked_amount_attributes = self.merge_tokens(payments, OptionalValue::None); let sft_nonce = self.get_or_create_nonce_for_attributes( &locked_token_mapper, - &output_pair.attributes.original_token_id.clone().into_name(), - &output_pair.attributes, + &new_locked_amount_attributes + .attributes + .original_token_id + .clone() + .into_name(), + &new_locked_amount_attributes.attributes, ); - let new_locked_tokens = - locked_token_mapper.nft_add_quantity(sft_nonce, output_pair.token_amount.clone()); + let new_locked_tokens = locked_token_mapper + .nft_add_quantity(sft_nonce, new_locked_amount_attributes.token_amount.clone()); self.fees_from_penalty_unlocking().set(NonceAmountPair::new( new_locked_tokens.token_nonce, @@ -277,19 +267,27 @@ pub trait UnlockWithPenaltyModule: #[payable("*")] #[endpoint(sendFeesToCollector)] fn send_fees_to_collector(&self) { - let sc_address = self.fees_collector_address().get(); - let locked_token_id = self.locked_token().get_token_id(); - let nonce_amount_pair = self.fees_from_penalty_unlocking().get(); - - self.fees_from_penalty_unlocking().clear(); - self.fees_collector_proxy_builder(sc_address) - .deposit_swap_fees() - .add_esdt_token_transfer( - locked_token_id, - nonce_amount_pair.nonce, - nonce_amount_pair.amount, - ) - .execute_on_dest_context_ignore_result(); + // Send fees to FeeCollector SC + let current_epoch = self.blockchain().get_block_epoch(); + let last_week_fee_sent_to_collector = self.last_week_fee_sent_to_collector().get(); + + if current_epoch >= last_week_fee_sent_to_collector + EPOCHS_PER_WEEK { + let sc_address = self.fees_collector_address().get(); + let locked_token_id = self.locked_token().get_token_id(); + let nonce_amount_pair = self.fees_from_penalty_unlocking().get(); + + self.fees_from_penalty_unlocking().clear(); + self.fees_collector_proxy_builder(sc_address) + .deposit_swap_fees() + .add_esdt_token_transfer( + locked_token_id, + nonce_amount_pair.nonce, + nonce_amount_pair.amount, + ) + .execute_on_dest_context_ignore_result(); + + self.last_week_fee_sent_to_collector().set(current_epoch); + } } #[proxy] diff --git a/locked-asset/simple-lock-energy/tests/simple_lock_energy_test.rs b/locked-asset/simple-lock-energy/tests/simple_lock_energy_test.rs index 860529d2c..b20e408c8 100644 --- a/locked-asset/simple-lock-energy/tests/simple_lock_energy_test.rs +++ b/locked-asset/simple-lock-energy/tests/simple_lock_energy_test.rs @@ -278,12 +278,12 @@ fn multiple_early_unlocks_same_week_test() { setup.b_mock.check_nft_balance( &setup.sc_wrapper.address_ref(), LOCKED_TOKEN_ID, - 1, + 2, &(expected_penalty_amount + 2u64), Some(&LockedTokenAttributes:: { original_token_id: managed_token_id_wrapped!(BASE_ASSET_TOKEN_ID), original_token_nonce: 0, - unlock_epoch: 360, + unlock_epoch: 390, }), ); } diff --git a/locked-asset/simple-lock-energy/tests/token_merging_test.rs b/locked-asset/simple-lock-energy/tests/token_merging_test.rs index 194ab19cb..676af01a3 100644 --- a/locked-asset/simple-lock-energy/tests/token_merging_test.rs +++ b/locked-asset/simple-lock-energy/tests/token_merging_test.rs @@ -51,7 +51,7 @@ fn token_merging_test() { setup .b_mock .execute_esdt_multi_transfer(&first_user, &setup.sc_wrapper, &payments[..], |sc| { - let _ = sc.merge_tokens(OptionalValue::None); + let _ = sc.merge_tokens_endpoint(OptionalValue::None); }) .assert_ok(); From e0afce7ba6e161759ee17bf5bb821416fca7ff71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Tue, 18 Oct 2022 10:31:15 +0300 Subject: [PATCH 10/20] Cache fees to energy contract Use sendFeesToCollector to send them to collector (by weekly basis) --- locked-asset/simple-lock-energy/src/unlock_with_penalty.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs b/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs index 92e2f0948..f30e936a0 100644 --- a/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs +++ b/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs @@ -26,7 +26,11 @@ pub mod fees_collector_proxy { #[payable("*")] #[endpoint(depositSwapFees)] fn deposit_swap_fees(&self); - } + + #[payable("*")] + #[endpoint(depositPenaltyFees)] + fn deposit_penalty_fees(&self); + } #[elrond_wasm::module] @@ -208,6 +212,7 @@ pub trait UnlockWithPenaltyModule: let fees_burn_percentage = self.fees_burn_percentage().get(); let burn_amount = fees_amount * fees_burn_percentage as u64 / MAX_PERCENTAGE as u64; let remaining_amount = fees_amount - &burn_amount; + let locked_token_mapper = self.locked_token(); if burn_amount > 0 { self.send() From 99a226258094ee7478be2f20b83d4a13bea47966 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Tue, 18 Oct 2022 11:02:01 +0300 Subject: [PATCH 11/20] Cache the loked tokens into storage Use fees_from_penalty_unlocking storage to cache fees --- locked-asset/simple-lock-energy/src/unlock_with_penalty.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs b/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs index f30e936a0..3916d6bc8 100644 --- a/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs +++ b/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs @@ -30,7 +30,7 @@ pub mod fees_collector_proxy { #[payable("*")] #[endpoint(depositPenaltyFees)] fn deposit_penalty_fees(&self); - + } } #[elrond_wasm::module] From 9859a109048973f591acaae2b4a218a70eea1df4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Tue, 18 Oct 2022 17:45:16 +0300 Subject: [PATCH 12/20] Fixes after review --- .../common-modules/weekly-rewards-splitting/src/lib.rs | 2 +- locked-asset/simple-lock-energy/src/unlock_with_penalty.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/energy-integration/common-modules/weekly-rewards-splitting/src/lib.rs b/energy-integration/common-modules/weekly-rewards-splitting/src/lib.rs index 9bc90f14c..d40ab0b55 100644 --- a/energy-integration/common-modules/weekly-rewards-splitting/src/lib.rs +++ b/energy-integration/common-modules/weekly-rewards-splitting/src/lib.rs @@ -89,7 +89,6 @@ pub trait WeeklyRewardsSplittingModule: claim_progress.week = current_week; claim_progress.energy = current_user_energy; } - claim_progress_mapper.set(&claim_progress); self.emit_claim_multi_event( @@ -206,6 +205,7 @@ pub trait WeeklyRewardsSplittingModule: fn current_claim_progress( &self, user: &ManagedAddress, + nonce: Nonce, ) -> SingleValueMapper>; #[view(getUserEnergyForWeek)] diff --git a/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs b/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs index 3916d6bc8..af4d595f6 100644 --- a/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs +++ b/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs @@ -212,7 +212,6 @@ pub trait UnlockWithPenaltyModule: let fees_burn_percentage = self.fees_burn_percentage().get(); let burn_amount = fees_amount * fees_burn_percentage as u64 / MAX_PERCENTAGE as u64; let remaining_amount = fees_amount - &burn_amount; - let locked_token_mapper = self.locked_token(); if burn_amount > 0 { self.send() @@ -269,6 +268,7 @@ pub trait UnlockWithPenaltyModule: )); } + #[payable("*")] #[endpoint(sendFeesToCollector)] fn send_fees_to_collector(&self) { From 51b8bae99120d292ae94486e633d869a046e525c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 19 Oct 2022 09:26:03 +0300 Subject: [PATCH 13/20] Fixed compiling issues --- .../common-modules/weekly-rewards-splitting/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/energy-integration/common-modules/weekly-rewards-splitting/src/lib.rs b/energy-integration/common-modules/weekly-rewards-splitting/src/lib.rs index d40ab0b55..723e7f85e 100644 --- a/energy-integration/common-modules/weekly-rewards-splitting/src/lib.rs +++ b/energy-integration/common-modules/weekly-rewards-splitting/src/lib.rs @@ -89,6 +89,7 @@ pub trait WeeklyRewardsSplittingModule: claim_progress.week = current_week; claim_progress.energy = current_user_energy; } + claim_progress_mapper.set(&claim_progress); self.emit_claim_multi_event( @@ -147,7 +148,7 @@ pub trait WeeklyRewardsSplittingModule: let total_energy = self.total_energy_for_week(week).get(); for weekly_reward in total_rewards { - let reward_amount = weekly_reward.amount * energy_amount / &total_energy; + let reward_amount = weekly_reward.amount * &energy_amount / &total_energy; if reward_amount > 0 { user_rewards.push(EsdtTokenPayment::new( weekly_reward.token_identifier, @@ -205,7 +206,6 @@ pub trait WeeklyRewardsSplittingModule: fn current_claim_progress( &self, user: &ManagedAddress, - nonce: Nonce, ) -> SingleValueMapper>; #[view(getUserEnergyForWeek)] From 1126015928b7353a90e3bd3f4b46ab8ef8a34b30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 19 Oct 2022 13:33:30 +0300 Subject: [PATCH 14/20] Fix existing tests and create 2 new tests --- locked-asset/simple-lock-energy/src/unlock_with_penalty.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs b/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs index af4d595f6..8c4146e0c 100644 --- a/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs +++ b/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs @@ -268,7 +268,6 @@ pub trait UnlockWithPenaltyModule: )); } - #[payable("*")] #[endpoint(sendFeesToCollector)] fn send_fees_to_collector(&self) { From 2f3969edae257f9411208db64713c385f86727ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 19 Oct 2022 15:47:20 +0300 Subject: [PATCH 15/20] Fix conflicts --- .../common-modules/weekly-rewards-splitting/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/energy-integration/common-modules/weekly-rewards-splitting/src/lib.rs b/energy-integration/common-modules/weekly-rewards-splitting/src/lib.rs index 723e7f85e..9bc90f14c 100644 --- a/energy-integration/common-modules/weekly-rewards-splitting/src/lib.rs +++ b/energy-integration/common-modules/weekly-rewards-splitting/src/lib.rs @@ -148,7 +148,7 @@ pub trait WeeklyRewardsSplittingModule: let total_energy = self.total_energy_for_week(week).get(); for weekly_reward in total_rewards { - let reward_amount = weekly_reward.amount * &energy_amount / &total_energy; + let reward_amount = weekly_reward.amount * energy_amount / &total_energy; if reward_amount > 0 { user_rewards.push(EsdtTokenPayment::new( weekly_reward.token_identifier, From 59a2fc34f0b012b1c59c6eea22f867f5d2fcc7c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 19 Oct 2022 18:55:05 +0300 Subject: [PATCH 16/20] Fix conflicts --- .../src/global_info.rs | 4 ++-- .../src/unlock_with_penalty.rs | 2 +- .../tests/simple_lock_energy_test.rs | 22 +++++++++---------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/energy-integration/common-modules/weekly-rewards-splitting/src/global_info.rs b/energy-integration/common-modules/weekly-rewards-splitting/src/global_info.rs index 7ec15d45a..483abfc2c 100644 --- a/energy-integration/common-modules/weekly-rewards-splitting/src/global_info.rs +++ b/energy-integration/common-modules/weekly-rewards-splitting/src/global_info.rs @@ -1,6 +1,6 @@ elrond_wasm::imports!(); -use common_types::{TokenAmountPair, Week}; +use common_types::Week; use energy_query::Energy; use week_timekeeping::EPOCHS_IN_WEEK; @@ -73,7 +73,7 @@ pub trait WeeklyRewardsGlobalInfo: crate::events::WeeklyRewardsSplittingEventsMo fn total_rewards_for_week( &self, week: Week, - ) -> SingleValueMapper>>; + ) -> SingleValueMapper>>; #[view(getTotalEnergyForWeek)] #[storage_mapper("totalEnergyForWeek")] diff --git a/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs b/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs index 8c4146e0c..e80ac38e1 100644 --- a/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs +++ b/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs @@ -5,7 +5,7 @@ use common_structs::{Epoch, Nonce, NonceAmountPair, PaymentsVec, Week}; use simple_lock::locked_token::LockedTokenAttributes; -use crate::{lock_options::EPOCHS_PER_MONTH, token_merging}; +use crate::{lock_options::{EPOCHS_PER_YEAR}, token_merging}; const MAX_PERCENTAGE: u16 = 10_000; // 100% const MIN_EPOCHS_TO_REDUCE: Epoch = 1; diff --git a/locked-asset/simple-lock-energy/tests/simple_lock_energy_test.rs b/locked-asset/simple-lock-energy/tests/simple_lock_energy_test.rs index b20e408c8..6afd8bc21 100644 --- a/locked-asset/simple-lock-energy/tests/simple_lock_energy_test.rs +++ b/locked-asset/simple-lock-energy/tests/simple_lock_energy_test.rs @@ -197,9 +197,9 @@ fn multiple_early_unlocks_same_week_test() { current_epoch += half_year_epochs; setup.b_mock.set_block_epoch(current_epoch); - let mut penalty_percentage = 485u64; // 1 + 9_999 * 177 / (10 * 365) ~= 1 + 484 = 485 + let mut penalty_percentage = 498u64; // 1 + 9_999 * 177 / (10 * 365) ~= 1 + 484 = 485 let mut expected_penalty_amount = rust_biguint!(sixth_balance) * penalty_percentage / 10_000u64; - let mut penalty_amount = setup.get_penalty_amount(sixth_balance, 177); + let mut penalty_amount = setup.get_penalty_amount(sixth_balance, 179); assert_eq!(penalty_amount, expected_penalty_amount); // Unlock early 1/3 of the LockedTokens @@ -231,9 +231,9 @@ fn multiple_early_unlocks_same_week_test() { .unlock_early(&first_user, 1, sixth_balance) .assert_ok(); - penalty_percentage = 485u64; // 1 + 9_999 * 177 / (10 * 365) ~= 1 + 484 = 485 + penalty_percentage = 498u64; // 1 + 9_999 * 177 / (10 * 365) ~= 1 + 484 = 485 expected_penalty_amount = rust_biguint!(sixth_balance) * penalty_percentage / 10_000u64; - penalty_amount = setup.get_penalty_amount(sixth_balance, 177); + penalty_amount = setup.get_penalty_amount(sixth_balance, 179); assert_eq!(penalty_amount, expected_penalty_amount); let received_token_amount_2 = rust_biguint!(sixth_balance) - penalty_amount; @@ -261,9 +261,9 @@ fn multiple_early_unlocks_same_week_test() { .unlock_early(&first_user, 1, sixth_balance) .assert_ok(); - penalty_percentage = 485u64; // 1 + 9_999 * 177 / (10 * 365) ~= 1 + 484 = 485 + penalty_percentage = 498u64; // 1 + 9_999 * 177 / (10 * 365) ~= 1 + 484 = 485 expected_penalty_amount = rust_biguint!(sixth_balance) * penalty_percentage / 10_000u64; - penalty_amount = setup.get_penalty_amount(sixth_balance, 177); + penalty_amount = setup.get_penalty_amount(sixth_balance, 179); assert_eq!(penalty_amount, expected_penalty_amount); let received_token_amount_3 = rust_biguint!(sixth_balance) - penalty_amount; @@ -314,9 +314,9 @@ fn multiple_early_unlocks_multiple_weeks_fee_collector_check_test() { current_epoch += half_year_epochs; setup.b_mock.set_block_epoch(current_epoch); - let mut penalty_percentage = 485u64; // 1 + 9_999 * 177 / (10 * 365) ~= 1 + 484 = 485 + let mut penalty_percentage = 498u64; // 1 + 9_999 * 177 / (10 * 365) ~= 1 + 484 = 485 let expected_penalty_amount = rust_biguint!(quarter_balance) * penalty_percentage / 10_000u64; - let mut penalty_amount = setup.get_penalty_amount(quarter_balance, 177); + let mut penalty_amount = setup.get_penalty_amount(quarter_balance, 179); assert_eq!(penalty_amount, expected_penalty_amount); // Unlock early half of the LockedTokens @@ -350,9 +350,9 @@ fn multiple_early_unlocks_multiple_weeks_fee_collector_check_test() { .unlock_early(&first_user, 1, quarter_balance) .assert_ok(); - penalty_percentage = 466u64; // 1 + 9_999 * 170 / (10 * 365) ~= 1 + 465 = 466 + penalty_percentage = 478u64; // 1 + 9_999 * 172 / (10 * 360) ~= 1 + 465 = 466 let expected_penalty_amount_2 = rust_biguint!(quarter_balance) * penalty_percentage / 10_000u64; - penalty_amount = setup.get_penalty_amount(quarter_balance, 170); + penalty_amount = setup.get_penalty_amount(quarter_balance, 172); assert_eq!(penalty_amount, expected_penalty_amount_2); let received_token_amount_2 = rust_biguint!(quarter_balance) - penalty_amount; @@ -432,7 +432,7 @@ fn reduce_lock_period_test() { Some(&LockedTokenAttributes:: { original_token_id: managed_token_id_wrapped!(BASE_ASSET_TOKEN_ID), original_token_nonce: 0, - unlock_epoch: 360, + unlock_epoch: 1800, }), ); From e93cbb4e1a894a0fa73dc2274c59be315052fb96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Wed, 19 Oct 2022 19:07:41 +0300 Subject: [PATCH 17/20] Fix conflicts --- locked-asset/simple-lock-energy/src/unlock_with_penalty.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs b/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs index e80ac38e1..bd2cb5a9e 100644 --- a/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs +++ b/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs @@ -5,7 +5,7 @@ use common_structs::{Epoch, Nonce, NonceAmountPair, PaymentsVec, Week}; use simple_lock::locked_token::LockedTokenAttributes; -use crate::{lock_options::{EPOCHS_PER_YEAR}, token_merging}; +use crate::{lock_options::EPOCHS_PER_YEAR, token_merging}; const MAX_PERCENTAGE: u16 = 10_000; // 100% const MIN_EPOCHS_TO_REDUCE: Epoch = 1; From 31dc6430b6e5b8cfa722964706f7e24a5e2e042d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Thu, 20 Oct 2022 10:58:44 +0300 Subject: [PATCH 18/20] Fixes after review --- .../simple-lock-energy/src/token_merging.rs | 77 +++++++++---------- .../src/unlock_with_penalty.rs | 7 +- 2 files changed, 38 insertions(+), 46 deletions(-) diff --git a/locked-asset/simple-lock-energy/src/token_merging.rs b/locked-asset/simple-lock-energy/src/token_merging.rs index 9c412b3e8..193f07643 100644 --- a/locked-asset/simple-lock-energy/src/token_merging.rs +++ b/locked-asset/simple-lock-energy/src/token_merging.rs @@ -90,53 +90,50 @@ pub trait TokenMergingModule: let first_payment = payments.get(0); payments.remove(0); - let output_amount_attributes = - self.update_energy(&original_caller, |energy: &mut Energy| { - let first_token_attributes: LockedTokenAttributes = - locked_token_mapper.get_token_attributes(first_payment.token_nonce); + self.update_energy(&original_caller, |energy: &mut Energy| { + let first_token_attributes: LockedTokenAttributes = + locked_token_mapper.get_token_attributes(first_payment.token_nonce); + energy.update_after_unlock_any( + &first_payment.amount, + first_token_attributes.unlock_epoch, + current_epoch, + ); + + locked_token_mapper.nft_burn(first_payment.token_nonce, &first_payment.amount); + + let mut output_pair = LockedAmountAttributesPair { + token_amount: first_payment.amount, + attributes: first_token_attributes, + }; + for payment in &payments { + let attributes: LockedTokenAttributes = + locked_token_mapper.get_token_attributes(payment.token_nonce); energy.update_after_unlock_any( - &first_payment.amount, - first_token_attributes.unlock_epoch, + &payment.amount, + attributes.unlock_epoch, current_epoch, ); - locked_token_mapper.nft_burn(first_payment.token_nonce, &first_payment.amount); + locked_token_mapper.nft_burn(payment.token_nonce, &payment.amount); - let mut output_pair = LockedAmountAttributesPair { - token_amount: first_payment.amount, - attributes: first_token_attributes, + let amount_attr_pair = LockedAmountAttributesPair { + token_amount: payment.amount, + attributes, }; - for payment in &payments { - let attributes: LockedTokenAttributes = - locked_token_mapper.get_token_attributes(payment.token_nonce); - energy.update_after_unlock_any( - &payment.amount, - attributes.unlock_epoch, - current_epoch, - ); - - locked_token_mapper.nft_burn(payment.token_nonce, &payment.amount); - - let amount_attr_pair = LockedAmountAttributesPair { - token_amount: payment.amount, - attributes, - }; - output_pair.merge_with(amount_attr_pair); - } - - let normalized_unlock_epoch = self.unlock_epoch_to_start_of_month_upper_estimate( - output_pair.attributes.unlock_epoch, - ); - output_pair.attributes.unlock_epoch = normalized_unlock_epoch; + output_pair.merge_with(amount_attr_pair); + } - energy.add_after_token_lock( - &output_pair.token_amount, - output_pair.attributes.unlock_epoch, - current_epoch, - ); + let normalized_unlock_epoch = self + .unlock_epoch_to_start_of_month_upper_estimate(output_pair.attributes.unlock_epoch); + output_pair.attributes.unlock_epoch = normalized_unlock_epoch; + + energy.add_after_token_lock( + &output_pair.token_amount, + output_pair.attributes.unlock_epoch, + current_epoch, + ); - output_pair - }); - output_amount_attributes + output_pair + }) } } diff --git a/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs b/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs index bd2cb5a9e..04ec06b4b 100644 --- a/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs +++ b/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs @@ -26,10 +26,6 @@ pub mod fees_collector_proxy { #[payable("*")] #[endpoint(depositSwapFees)] fn deposit_swap_fees(&self); - - #[payable("*")] - #[endpoint(depositPenaltyFees)] - fn deposit_penalty_fees(&self); } } @@ -260,7 +256,7 @@ pub trait UnlockWithPenaltyModule: ); let new_locked_tokens = locked_token_mapper - .nft_add_quantity(sft_nonce, new_locked_amount_attributes.token_amount.clone()); + .nft_add_quantity(sft_nonce, new_locked_amount_attributes.token_amount); self.fees_from_penalty_unlocking().set(NonceAmountPair::new( new_locked_tokens.token_nonce, @@ -268,7 +264,6 @@ pub trait UnlockWithPenaltyModule: )); } - #[payable("*")] #[endpoint(sendFeesToCollector)] fn send_fees_to_collector(&self) { // Send fees to FeeCollector SC From 3bad448af85dafab4d8574447c1ded31265ddc86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Thu, 20 Oct 2022 12:13:17 +0300 Subject: [PATCH 19/20] Fixes after review --- common/common_structs/src/alias_types.rs | 2 +- .../weekly-rewards-splitting/src/lib.rs | 3 +- .../src/unlock_with_penalty.rs | 47 ++++++++++--------- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/common/common_structs/src/alias_types.rs b/common/common_structs/src/alias_types.rs index 750c00fc2..4e785e28e 100644 --- a/common/common_structs/src/alias_types.rs +++ b/common/common_structs/src/alias_types.rs @@ -4,7 +4,7 @@ use crate::{LockedAssetTokenAttributesEx, UnlockSchedule}; pub type Nonce = u64; pub type Epoch = u64; -pub type Week = u64; +pub type Week = usize; pub type PaymentsVec = ManagedVec>; pub type UnlockPeriod = UnlockSchedule; pub type OldLockedTokenAttributes = LockedAssetTokenAttributesEx; diff --git a/energy-integration/common-modules/weekly-rewards-splitting/src/lib.rs b/energy-integration/common-modules/weekly-rewards-splitting/src/lib.rs index 9bc90f14c..fde012447 100644 --- a/energy-integration/common-modules/weekly-rewards-splitting/src/lib.rs +++ b/energy-integration/common-modules/weekly-rewards-splitting/src/lib.rs @@ -140,9 +140,8 @@ pub trait WeeklyRewardsSplittingModule: energy_amount: &BigUint, total_rewards: &PaymentsVec, ) -> PaymentsVec { - let biguint_zero = BigUint::zero(); let mut user_rewards = ManagedVec::new(); - if energy_amount == &biguint_zero { + if energy_amount == &0 { return user_rewards; } diff --git a/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs b/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs index 04ec06b4b..62c9fbcdb 100644 --- a/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs +++ b/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs @@ -1,7 +1,7 @@ elrond_wasm::imports!(); elrond_wasm::derive_imports!(); -use common_structs::{Epoch, Nonce, NonceAmountPair, PaymentsVec, Week}; +use common_structs::{Epoch, Nonce, NonceAmountPair, PaymentsVec}; use simple_lock::locked_token::LockedTokenAttributes; @@ -268,25 +268,28 @@ pub trait UnlockWithPenaltyModule: fn send_fees_to_collector(&self) { // Send fees to FeeCollector SC let current_epoch = self.blockchain().get_block_epoch(); - let last_week_fee_sent_to_collector = self.last_week_fee_sent_to_collector().get(); - - if current_epoch >= last_week_fee_sent_to_collector + EPOCHS_PER_WEEK { - let sc_address = self.fees_collector_address().get(); - let locked_token_id = self.locked_token().get_token_id(); - let nonce_amount_pair = self.fees_from_penalty_unlocking().get(); - - self.fees_from_penalty_unlocking().clear(); - self.fees_collector_proxy_builder(sc_address) - .deposit_swap_fees() - .add_esdt_token_transfer( - locked_token_id, - nonce_amount_pair.nonce, - nonce_amount_pair.amount, - ) - .execute_on_dest_context_ignore_result(); - - self.last_week_fee_sent_to_collector().set(current_epoch); + let last_week_fee_sent_to_collector = self.last_epoch_fee_sent_to_collector().get(); + let next_send_epoch = last_week_fee_sent_to_collector as u64 + EPOCHS_PER_WEEK; + + if current_epoch < next_send_epoch { + return; } + + let sc_address = self.fees_collector_address().get(); + let locked_token_id = self.locked_token().get_token_id(); + let nonce_amount_pair = self.fees_from_penalty_unlocking().get(); + + self.fees_from_penalty_unlocking().clear(); + self.fees_collector_proxy_builder(sc_address) + .deposit_swap_fees() + .add_esdt_token_transfer( + locked_token_id, + nonce_amount_pair.nonce, + nonce_amount_pair.amount, + ) + .execute_on_dest_context_ignore_result(); + + self.last_epoch_fee_sent_to_collector().set(current_epoch); } #[proxy] @@ -311,7 +314,7 @@ pub trait UnlockWithPenaltyModule: #[storage_mapper("feesFromPenaltyUnlocking")] fn fees_from_penalty_unlocking(&self) -> SingleValueMapper>; - #[view(getLastWeekFeeSentToCollector)] - #[storage_mapper("lastWeekFeeSentToCollector")] - fn last_week_fee_sent_to_collector(&self) -> SingleValueMapper; + #[view(getLastEpochFeeSentToCollector)] + #[storage_mapper("lastEpochFeeSentToCollector")] + fn last_epoch_fee_sent_to_collector(&self) -> SingleValueMapper; } From 793bdb9b58880bd6eacb9965101151d4c979b97c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Costin=20Caraba=C8=99?= Date: Thu, 20 Oct 2022 12:41:45 +0300 Subject: [PATCH 20/20] Update locked-asset/simple-lock-energy/src/unlock_with_penalty.rs Co-authored-by: dorin-iancu <72252689+dorin-iancu@users.noreply.github.com> --- locked-asset/simple-lock-energy/src/unlock_with_penalty.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs b/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs index 62c9fbcdb..592ecf85c 100644 --- a/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs +++ b/locked-asset/simple-lock-energy/src/unlock_with_penalty.rs @@ -268,8 +268,8 @@ pub trait UnlockWithPenaltyModule: fn send_fees_to_collector(&self) { // Send fees to FeeCollector SC let current_epoch = self.blockchain().get_block_epoch(); - let last_week_fee_sent_to_collector = self.last_epoch_fee_sent_to_collector().get(); - let next_send_epoch = last_week_fee_sent_to_collector as u64 + EPOCHS_PER_WEEK; + let last_epoch_fee_sent_to_collector = self.last_epoch_fee_sent_to_collector().get(); + let next_send_epoch = last_epoch_fee_sent_to_collector + EPOCHS_PER_WEEK; if current_epoch < next_send_epoch { return;