Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

farm supply for week fix #938

Merged
merged 15 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 14 additions & 17 deletions dex/farm-with-locked-rewards/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,30 +127,19 @@ pub trait Farm:

self.migrate_old_farm_positions(&orig_caller);

let payments = self.call_value().all_esdt_transfers().clone_value();
let base_claim_rewards_result =
self.claim_rewards_base::<NoMintWrapper<Self>>(orig_caller.clone(), payments);
let output_farm_token_payment = base_claim_rewards_result.new_farm_token.payment.clone();
self.send_payment_non_zero(&caller, &output_farm_token_payment);
let claim_rewards_result = self.claim_rewards::<NoMintWrapper<Self>>(orig_caller.clone());

let rewards_payment = base_claim_rewards_result.rewards;
self.send_payment_non_zero(&caller, &claim_rewards_result.new_farm_token);

let rewards_payment = claim_rewards_result.rewards;
let locked_rewards_payment = self.send_to_lock_contract_non_zero(
rewards_payment.token_identifier,
rewards_payment.amount,
caller,
orig_caller.clone(),
);

self.emit_claim_rewards_event::<_, FarmTokenAttributes<Self::Api>>(
&orig_caller,
base_claim_rewards_result.context,
base_claim_rewards_result.new_farm_token,
locked_rewards_payment.clone(),
base_claim_rewards_result.created_with_merge,
base_claim_rewards_result.storage_cache,
orig_caller,
);

(output_farm_token_payment, locked_rewards_payment).into()
(claim_rewards_result.new_farm_token, locked_rewards_payment).into()
}

#[payable("*")]
Expand Down Expand Up @@ -237,7 +226,14 @@ pub trait Farm:
);
}

let mut storage_cache = StorageCache::new(self);
self.validate_contract_state(storage_cache.contract_state, &storage_cache.farm_token_id);
NoMintWrapper::<Self>::generate_aggregated_rewards(self, &mut storage_cache);

let boosted_rewards = self.claim_only_boosted_payment(user);

self.set_farm_supply_for_current_week(&storage_cache.farm_token_supply);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we have to do this every single time ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commented above. Also, this is how it already works for the other endpoints (enter, exit, claim).


self.send_to_lock_contract_non_zero(
self.reward_token_id().get(),
boosted_rewards,
Expand Down Expand Up @@ -270,6 +266,7 @@ pub trait Farm:
require!(percentage <= MAX_PERCENT, "Invalid percentage");

let mut storage_cache = StorageCache::new(self);
self.validate_contract_state(storage_cache.contract_state, &storage_cache.farm_token_id);
NoMintWrapper::<Self>::generate_aggregated_rewards(self, &mut storage_cache);

self.boosted_yields_rewards_percentage().set(percentage);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use farm_with_locked_rewards::Farm;
use locking_module::lock_with_energy_module::LockWithEnergyModule;
use multiversx_sc_modules::pause::PauseModule;
use pausable::{PausableModule, State};
use rewards::RewardsModule;
use sc_whitelist_module::SCWhitelistModule;
use simple_lock::locked_token::LockedTokenModule;
use week_timekeeping::Epoch;
Expand Down Expand Up @@ -422,4 +423,53 @@ where
)
.assert_ok();
}

pub fn claim_boosted_rewards_for_user(
&mut self,
owner: &Address,
broker: &Address,
locked_reward_nonce: u64,
) -> u64 {
self.last_farm_token_nonce += 1;

let mut result = 0;
self.b_mock
.execute_tx(broker, &self.farm_wrapper, &rust_biguint!(0), |sc| {
let reward_payment =
sc.claim_boosted_rewards(OptionalValue::Some(managed_address!(owner)));
assert_eq!(
reward_payment.token_identifier,
managed_token_id!(LOCKED_REWARD_TOKEN_ID)
);
assert_eq!(reward_payment.token_nonce, locked_reward_nonce);

result = reward_payment.amount.to_u64().unwrap();
})
.assert_ok();

result
}

pub fn check_farm_token_supply(&mut self, expected_farm_token_supply: u64) {
let b_mock = &mut self.b_mock;
b_mock
.execute_query(&self.farm_wrapper, |sc| {
let actual_farm_supply = sc.farm_token_supply().get();
assert_eq!(
managed_biguint!(expected_farm_token_supply),
actual_farm_supply
);
})
.assert_ok();
}

pub fn check_farm_rps(&mut self, expected_amount: u64) {
let b_mock = &mut self.b_mock;
b_mock
.execute_query(&self.farm_wrapper, |sc| {
let current_rps = sc.reward_per_share().get();
assert_eq!(managed_biguint!(expected_amount), current_rps);
})
.assert_ok();
}
}
193 changes: 193 additions & 0 deletions dex/farm-with-locked-rewards/tests/farm_with_locked_rewards_test.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![allow(deprecated)]

use common_structs::FarmTokenAttributes;
use multiversx_sc::codec::Empty;
use multiversx_sc_scenario::{managed_address, managed_biguint, rust_biguint, DebugApi};
use simple_lock::locked_token::LockedTokenAttributes;

Expand Down Expand Up @@ -335,3 +336,195 @@ fn total_farm_position_claim_with_locked_rewards_test() {
None,
);
}

#[test]
fn claim_only_boosted_rewards_per_week_test() {
DebugApi::dummy();
let mut farm_setup = FarmSetup::new(
farm_with_locked_rewards::contract_obj,
energy_factory::contract_obj,
);

farm_setup.set_boosted_yields_rewards_percentage(BOOSTED_YIELDS_PERCENTAGE);
farm_setup.set_boosted_yields_factors();
farm_setup.b_mock.set_block_epoch(2);

let temp_user = farm_setup.third_user.clone();

// first user enter farm
let farm_in_amount = 100_000_000;
let first_user = farm_setup.first_user.clone();
farm_setup.set_user_energy(&first_user, 1_000, 2, 1);
farm_setup.enter_farm(&first_user, farm_in_amount);

farm_setup.check_farm_token_supply(farm_in_amount);
farm_setup.check_farm_rps(0u64);

farm_setup.b_mock.set_block_nonce(10);
farm_setup.b_mock.set_block_epoch(6);
farm_setup.set_user_energy(&first_user, 1_000, 6, 1);
farm_setup.set_user_energy(&temp_user, 1, 6, 1);
farm_setup.enter_farm(&temp_user, 1);
farm_setup.exit_farm(&temp_user, 2, 1);

farm_setup.check_farm_rps(75_000_000u64);

// advance 1 week
farm_setup.set_user_energy(&first_user, 1_000, 13, 1);
farm_setup.b_mock.set_block_nonce(20);
farm_setup.b_mock.set_block_epoch(13);

let boosted_rewards = 2_500;
let second_week_received_reward_amt =
farm_setup.claim_boosted_rewards_for_user(&first_user, &first_user, 1);

assert_eq!(second_week_received_reward_amt, boosted_rewards);
farm_setup.check_farm_rps(150_000_000u64);

// advance 1 week
farm_setup.set_user_energy(&first_user, 1_000, 15, 1);
farm_setup.b_mock.set_block_nonce(30);
farm_setup.b_mock.set_block_epoch(15);
let third_week_received_reward_amt =
farm_setup.claim_boosted_rewards_for_user(&first_user, &first_user, 1);

assert_eq!(third_week_received_reward_amt, boosted_rewards);
farm_setup.check_farm_rps(225_000_000u64);

farm_setup.b_mock.check_nft_balance::<Empty>(
&first_user,
LOCKED_REWARD_TOKEN_ID,
1,
&rust_biguint!(boosted_rewards * 2),
None,
);
}

#[test]
fn claim_rewards_per_week_test() {
DebugApi::dummy();
let mut farm_setup = FarmSetup::new(
farm_with_locked_rewards::contract_obj,
energy_factory::contract_obj,
);

farm_setup.set_boosted_yields_rewards_percentage(BOOSTED_YIELDS_PERCENTAGE);
farm_setup.set_boosted_yields_factors();
farm_setup.b_mock.set_block_epoch(2);

let temp_user = farm_setup.third_user.clone();

// first user enter farm
let farm_in_amount = 100_000_000;
let first_user = farm_setup.first_user.clone();
farm_setup.set_user_energy(&first_user, 1_000, 2, 1);
farm_setup.enter_farm(&first_user, farm_in_amount);

farm_setup.check_farm_token_supply(farm_in_amount);
farm_setup.check_farm_rps(0u64);

farm_setup.b_mock.set_block_nonce(10);
farm_setup.b_mock.set_block_epoch(6);
farm_setup.set_user_energy(&first_user, 1_000, 6, 1);
farm_setup.set_user_energy(&temp_user, 1, 6, 1);
farm_setup.enter_farm(&temp_user, 1);
farm_setup.exit_farm(&temp_user, 2, 1);

farm_setup.check_farm_rps(75_000_000u64);
let base_rewards_per_week = 7_500;
let boosted_rewards_per_week = 2_500;
let total_rewards_per_week = base_rewards_per_week + boosted_rewards_per_week;

// advance 1 week
farm_setup.set_user_energy(&first_user, 1_000, 13, 1);
farm_setup.b_mock.set_block_nonce(20);
farm_setup.b_mock.set_block_epoch(13);

let second_week_received_reward_amt = farm_setup.claim_rewards(&first_user, 1, farm_in_amount);

assert_eq!(
second_week_received_reward_amt,
total_rewards_per_week + base_rewards_per_week
);
farm_setup.check_farm_rps(150_000_000u64);

// advance 1 week
farm_setup.set_user_energy(&first_user, 1_000, 15, 1);
farm_setup.b_mock.set_block_nonce(30);
farm_setup.b_mock.set_block_epoch(15);
let third_week_received_reward_amt = farm_setup.claim_rewards(&first_user, 3, farm_in_amount);

assert_eq!(third_week_received_reward_amt, total_rewards_per_week);
farm_setup.check_farm_rps(225_000_000u64);

farm_setup.b_mock.check_nft_balance::<Empty>(
&first_user,
LOCKED_REWARD_TOKEN_ID,
1,
&rust_biguint!(total_rewards_per_week * 2 + base_rewards_per_week),
None,
);
}

#[test]
fn claim_boosted_rewards_with_zero_position_test() {
DebugApi::dummy();
let mut farm_setup = FarmSetup::new(
farm_with_locked_rewards::contract_obj,
energy_factory::contract_obj,
);

farm_setup.set_boosted_yields_rewards_percentage(BOOSTED_YIELDS_PERCENTAGE);
farm_setup.set_boosted_yields_factors();
farm_setup.b_mock.set_block_epoch(2);

let temp_user = farm_setup.third_user.clone();

// first user enter farm
let farm_in_amount = 100_000_000;
let first_user = farm_setup.first_user.clone();
farm_setup.set_user_energy(&first_user, 1_000, 2, 1);
farm_setup.enter_farm(&first_user, farm_in_amount);

farm_setup.check_farm_token_supply(farm_in_amount);
farm_setup.check_farm_rps(0u64);

farm_setup.b_mock.set_block_nonce(10);
farm_setup.b_mock.set_block_epoch(6);
farm_setup.set_user_energy(&first_user, 1_000, 6, 1);
farm_setup.set_user_energy(&temp_user, 1, 6, 1);
farm_setup.enter_farm(&temp_user, 1);
farm_setup.exit_farm(&temp_user, 2, 1);

farm_setup.check_farm_rps(75_000_000u64);

// advance 1 week
farm_setup.set_user_energy(&first_user, 1_000, 13, 1);
farm_setup.b_mock.set_block_nonce(20);
farm_setup.b_mock.set_block_epoch(13);

let second_week_received_reward_amt =
farm_setup.claim_boosted_rewards_for_user(&temp_user, &temp_user, 0);

assert_eq!(second_week_received_reward_amt, 0);
farm_setup.check_farm_rps(150_000_000u64);

// advance 1 week
let boosted_rewards = 2_500;
farm_setup.set_user_energy(&first_user, 1_000, 15, 1);
farm_setup.b_mock.set_block_nonce(30);
farm_setup.b_mock.set_block_epoch(15);
let third_week_received_reward_amt =
farm_setup.claim_boosted_rewards_for_user(&first_user, &first_user, 1);

assert_eq!(third_week_received_reward_amt, boosted_rewards * 2); // user receives rewards for weeks 1 and 2)
farm_setup.check_farm_rps(225_000_000u64);

farm_setup.b_mock.check_nft_balance::<Empty>(
&first_user,
LOCKED_REWARD_TOKEN_ID,
1,
&rust_biguint!(boosted_rewards * 2),
None,
);
}
7 changes: 7 additions & 0 deletions dex/farm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,16 @@ pub trait Farm:
);
}

let mut storage_cache = StorageCache::new(self);
self.validate_contract_state(storage_cache.contract_state, &storage_cache.farm_token_id);
Wrapper::<Self>::generate_aggregated_rewards(self, &mut storage_cache);

let boosted_rewards = self.claim_only_boosted_payment(user);
let boosted_rewards_payment =
EsdtTokenPayment::new(self.reward_token_id().get(), 0, boosted_rewards);

self.set_farm_supply_for_current_week(&storage_cache.farm_token_supply);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we have to do this every single time ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also update all the claim endpoints to pass through an if statement.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to do this on claim, only when the storage is empty.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about adding it but I don't think it will bring any real improvement. An if statement would require a few extra storage reads (we would need to compute the current week), so in the end gas won't be saved, but at the same time it would add some extra logic with the if statement, which in turn would complicate the review/audit/testing parts.


self.send_payment_non_zero(user, &boosted_rewards_payment);

boosted_rewards_payment
Expand Down Expand Up @@ -261,6 +267,7 @@ pub trait Farm:
require!(percentage <= MAX_PERCENT, "Invalid percentage");

let mut storage_cache = StorageCache::new(self);
self.validate_contract_state(storage_cache.contract_state, &storage_cache.farm_token_id);
Wrapper::<Self>::generate_aggregated_rewards(self, &mut storage_cache);

self.boosted_yields_rewards_percentage().set(percentage);
Expand Down
7 changes: 1 addition & 6 deletions farm-staking/farm-staking/src/base_impl_wrapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,8 @@ where
caller: &ManagedAddress<<<Self as FarmContract>::FarmSc as ContractBase>::Api>,
) -> BigUint<<<Self as FarmContract>::FarmSc as ContractBase>::Api> {
let user_total_farm_position = sc.user_total_farm_position(caller).get();
let mut boosted_rewards = BigUint::zero();

if user_total_farm_position > 0 {
boosted_rewards = sc.claim_boosted_yields_rewards(caller, user_total_farm_position);
}

boosted_rewards
sc.claim_boosted_yields_rewards(caller, user_total_farm_position)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
use contexts::storage_cache::StorageCache;
use farm_base_impl::base_traits_impl::FarmContract;

use crate::base_impl_wrapper::FarmStakingWrapper;

multiversx_sc::imports!();
Expand Down Expand Up @@ -38,10 +41,15 @@ pub trait ClaimOnlyBoostedStakingRewardsModule:
);
}

let mut storage_cache = StorageCache::new(self);
FarmStakingWrapper::<Self>::generate_aggregated_rewards(self, &mut storage_cache);

let boosted_rewards = self.claim_only_boosted_payment(user);
let boosted_rewards_payment =
EsdtTokenPayment::new(self.reward_token_id().get(), 0, boosted_rewards);

self.set_farm_supply_for_current_week(&storage_cache.farm_token_supply);

self.send_payment_non_zero(user, &boosted_rewards_payment);

boosted_rewards_payment
Expand Down
Loading
Loading