Skip to content

Commit

Permalink
Merge pull request #685 from ElrondNetwork/farm-staking-proxy-exit-am…
Browse files Browse the repository at this point in the history
…ount-fix

farm staking proxy exit amounts fix
  • Loading branch information
dorin-iancu authored Dec 20, 2022
2 parents 4caec83 + 086e41a commit 202d539
Show file tree
Hide file tree
Showing 4 changed files with 213 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use core::mem::swap;

use farm::{
base_functions::{ClaimRewardsResultType, ExitFarmResultType},
ProxyTrait as _,
ExitFarmWithPartialPosResultType, ProxyTrait as _,
};
use farm_staking::{
claim_stake_farm_rewards::ProxyTrait as _, stake_farm::ProxyTrait as _,
Expand Down Expand Up @@ -64,12 +64,13 @@ pub trait ExternalContractsInteractionsModule:
let orig_caller = self.blockchain().get_caller();
let lp_farm_token_id = self.lp_farm_token_id().get();
let lp_farm_address = self.lp_farm_address().get();
let exit_farm_result: ExitFarmResultType<Self::Api> = self
let exit_farm_result: ExitFarmWithPartialPosResultType<Self::Api> = self
.lp_farm_proxy_obj(lp_farm_address)
.exit_farm_endpoint(exit_amount, orig_caller)
.add_esdt_token_transfer(lp_farm_token_id, lp_farm_token_nonce, lp_farm_token_amount)
.execute_on_dest_context();
let (mut lp_tokens, mut lp_farm_rewards) = exit_farm_result.into_tuple();
let (mut lp_tokens, mut lp_farm_rewards, remaining_farm_tokens) =
exit_farm_result.into_tuple();
let expected_lp_token_id = self.lp_token_id().get();

self.swap_payments_if_wrong_order(
Expand All @@ -82,6 +83,7 @@ pub trait ExternalContractsInteractionsModule:
LpFarmExitResult {
lp_tokens,
lp_farm_rewards,
remaining_farm_tokens,
}
}

Expand Down
55 changes: 42 additions & 13 deletions farm-staking/farm-staking-proxy/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,12 +216,21 @@ pub trait FarmStakingProxy:
self.dual_yield_token().require_same_token(&payment_token);

let attributes = self.get_dual_yield_token_attributes(payment_nonce);
let lp_farm_token_amount =
self.get_lp_farm_token_amount_equivalent(&attributes, &payment_amount);
let total_for_nonce = attributes
.get_total_dual_yield_tokens_for_position()
.clone();
require!(
payment_amount == total_for_nonce,
"Must exit with full position as payment"
);
require!(exit_amount <= total_for_nonce, "Invalid exit amount");

let lp_farm_exit_amount =
self.get_lp_farm_token_amount_equivalent(&attributes, &exit_amount);
let lp_farm_exit_result = self.lp_farm_exit(
attributes.lp_farm_token_nonce,
lp_farm_token_amount,
exit_amount,
attributes.lp_farm_token_amount,
lp_farm_exit_amount,
);

let remove_liq_result = self.pair_remove_liquidity(
Expand All @@ -230,23 +239,38 @@ pub trait FarmStakingProxy:
pair_second_token_min_amount,
);

let staking_farm_token_amount =
self.get_staking_farm_token_amount_equivalent(&payment_amount);
let staking_farm_exit_amount = self.get_staking_farm_token_amount_equivalent(&exit_amount);
let staking_farm_exit_result = self.staking_farm_unstake(
remove_liq_result.staking_token_payment,
attributes.staking_farm_token_nonce,
staking_farm_token_amount,
staking_farm_exit_amount.clone(),
);
let unstake_result = self.send_unstake_payments(

let opt_new_dual_yield_tokens = if exit_amount != total_for_nonce {
let remaining_lp_farm_tokens = lp_farm_exit_result.remaining_farm_tokens.amount;
let remaining_staking_farm_tokens =
attributes.staking_farm_token_amount - staking_farm_exit_amount;
let new_dual_yield_tokens = self.create_dual_yield_tokens(
attributes.lp_farm_token_nonce,
remaining_lp_farm_tokens,
attributes.staking_farm_token_nonce,
remaining_staking_farm_tokens,
);

Some(new_dual_yield_tokens)
} else {
None
};

self.burn_dual_yield_tokens(payment_nonce, &payment_amount);

self.send_unstake_payments(
remove_liq_result.other_token_payment,
lp_farm_exit_result.lp_farm_rewards,
staking_farm_exit_result.staking_rewards,
staking_farm_exit_result.unbond_staking_farm_token,
);

self.burn_dual_yield_tokens(payment_nonce, &payment_amount);

unstake_result
opt_new_dual_yield_tokens,
)
}

fn send_unstake_payments(
Expand All @@ -255,6 +279,7 @@ pub trait FarmStakingProxy:
lp_farm_rewards: EsdtTokenPayment<Self::Api>,
staking_rewards: EsdtTokenPayment<Self::Api>,
unbond_staking_farm_token: EsdtTokenPayment<Self::Api>,
opt_new_dual_yield_tokens: Option<EsdtTokenPayment<Self::Api>>,
) -> UnstakeResult<Self::Api> {
let caller = self.blockchain().get_caller();
let mut user_payments = ManagedVec::new();
Expand All @@ -269,6 +294,10 @@ pub trait FarmStakingProxy:
}
user_payments.push(unbond_staking_farm_token);

if let Some(new_dual_yield_tokens) = opt_new_dual_yield_tokens {
user_payments.push(new_dual_yield_tokens)
}

self.send().direct_multi(&caller, &user_payments);

user_payments.into()
Expand Down
1 change: 1 addition & 0 deletions farm-staking/farm-staking-proxy/src/result_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub struct LpFarmClaimRewardsResult<M: ManagedTypeApi> {
pub struct LpFarmExitResult<M: ManagedTypeApi> {
pub lp_tokens: EsdtTokenPayment<M>,
pub lp_farm_rewards: EsdtTokenPayment<M>,
pub remaining_farm_tokens: EsdtTokenPayment<M>,
}

// staking farm
Expand Down
166 changes: 165 additions & 1 deletion farm-staking/farm-staking-proxy/tests/staking_farm_with_lp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ elrond_wasm::imports!();

use constants::*;
use elrond_wasm::elrond_codec::Empty;
use elrond_wasm_debug::{managed_biguint, rust_biguint, DebugApi};
use elrond_wasm_debug::{managed_biguint, managed_token_id, rust_biguint, DebugApi};
use farm_staking_proxy::dual_yield_token::DualYieldTokenAttributes;
use farm_staking_proxy::FarmStakingProxy;
use staking_farm_with_lp_staking_contract_interactions::*;

#[test]
Expand Down Expand Up @@ -200,6 +201,169 @@ fn unstake_through_proxy_after_claim() {
);
}

#[test]
fn unstake_partial_position_test() {
let _ = DebugApi::dummy();
let mut setup = FarmStakingSetup::new(
pair::contract_obj,
farm::contract_obj,
farm_staking::contract_obj,
farm_staking_proxy::contract_obj,
);

let expected_staking_token_amount = 1_001_000_000;
let dual_yield_token_nonce_after_stake =
setup.stake_farm_lp_proxy(1, USER_TOTAL_LP_TOKENS, 1, expected_staking_token_amount);

setup
.b_mock
.set_block_nonce(BLOCK_NONCE_AFTER_PAIR_SETUP + 20);
setup.b_mock.set_block_epoch(20);

let dual_yield_token_amount = 1_001_000_000;

// unstake with half position - wrong payment amount
setup
.b_mock
.execute_esdt_transfer(
&setup.user_addr,
&setup.proxy_wrapper,
DUAL_YIELD_TOKEN_ID,
dual_yield_token_nonce_after_stake,
&rust_biguint!(dual_yield_token_amount / 2),
|sc| {
let _ = sc.unstake_farm_tokens(
managed_biguint!(1),
managed_biguint!(1),
managed_biguint!(dual_yield_token_amount / 4),
);
},
)
.assert_user_error("Must exit with full position as payment");

// unstake with half position - ok
setup
.b_mock
.execute_esdt_transfer(
&setup.user_addr,
&setup.proxy_wrapper,
DUAL_YIELD_TOKEN_ID,
dual_yield_token_nonce_after_stake,
&rust_biguint!(dual_yield_token_amount),
|sc| {
let results = sc
.unstake_farm_tokens(
managed_biguint!(1),
managed_biguint!(1),
managed_biguint!(dual_yield_token_amount / 2),
)
.to_vec();

let wegld_payment = results.get(0);
assert_eq!(
wegld_payment.token_identifier,
managed_token_id!(WEGLD_TOKEN_ID)
);
assert_eq!(wegld_payment.amount, 1_001_000_000 / 2);

let lp_farm_rewards = results.get(1);
assert_eq!(
lp_farm_rewards.token_identifier,
managed_token_id!(RIDE_TOKEN_ID)
);
assert_eq!(lp_farm_rewards.amount, 99_999 / 2);

let staking_rewards = results.get(2);
assert_eq!(
staking_rewards.token_identifier,
managed_token_id!(RIDE_TOKEN_ID)
);
assert_eq!(staking_rewards.amount, 1_899 / 2);

let unbond_tokens = results.get(3);
assert_eq!(
unbond_tokens.token_identifier,
managed_token_id!(STAKING_FARM_TOKEN_ID)
);
assert_eq!(unbond_tokens.amount, 1_001_000_000 / 2);

let new_dual_yield_tokens = results.get(4);
assert_eq!(
new_dual_yield_tokens.token_identifier,
managed_token_id!(DUAL_YIELD_TOKEN_ID)
);
assert_eq!(new_dual_yield_tokens.amount, 1_001_000_000 / 2);
},
)
.assert_ok();

let expected_new_dual_yield_attributes = DualYieldTokenAttributes::<DebugApi> {
lp_farm_token_nonce: 1,
lp_farm_token_amount: managed_biguint!(USER_TOTAL_LP_TOKENS / 2),
staking_farm_token_nonce: 1,
staking_farm_token_amount: managed_biguint!(1_001_000_000 / 2),
};
let new_dual_yield_token_nonce = dual_yield_token_nonce_after_stake + 1;
let new_dual_yield_token_amount = dual_yield_token_amount / 2;
setup.b_mock.check_nft_balance(
&setup.user_addr,
DUAL_YIELD_TOKEN_ID,
new_dual_yield_token_nonce,
&rust_biguint!(new_dual_yield_token_amount),
Some(&expected_new_dual_yield_attributes),
);

// unstake with the new dual yield tokens
setup
.b_mock
.execute_esdt_transfer(
&setup.user_addr,
&setup.proxy_wrapper,
DUAL_YIELD_TOKEN_ID,
new_dual_yield_token_nonce,
&rust_biguint!(new_dual_yield_token_amount),
|sc| {
let results = sc
.unstake_farm_tokens(
managed_biguint!(1),
managed_biguint!(1),
managed_biguint!(new_dual_yield_token_amount),
)
.to_vec();
assert_eq!(results.len(), 4); // no new dual yield tokens

let wegld_payment = results.get(0);
assert_eq!(
wegld_payment.token_identifier,
managed_token_id!(WEGLD_TOKEN_ID)
);
assert_eq!(wegld_payment.amount, 1_001_000_000 / 2);

let lp_farm_rewards = results.get(1);
assert_eq!(
lp_farm_rewards.token_identifier,
managed_token_id!(RIDE_TOKEN_ID)
);
assert_eq!(lp_farm_rewards.amount, 99_999 / 2);

let staking_rewards = results.get(2);
assert_eq!(
staking_rewards.token_identifier,
managed_token_id!(RIDE_TOKEN_ID)
);
assert_eq!(staking_rewards.amount, 1_899 / 2);

let unbond_tokens = results.get(3);
assert_eq!(
unbond_tokens.token_identifier,
managed_token_id!(STAKING_FARM_TOKEN_ID)
);
assert_eq!(unbond_tokens.amount, 1_001_000_000 / 2);
},
)
.assert_ok();
}

#[test]
fn unbond_test() {
let mut setup = FarmStakingSetup::new(
Expand Down

0 comments on commit 202d539

Please sign in to comment.