diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index fd870762f..bf93561f9 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -25,4 +25,4 @@ jobs: rust-toolchain: stable coverage-args: --ignore-filename-regex='/.cargo/git' --output ./coverage.md secrets: - token: ${{ secrets.GITHUB_TOKEN }} + token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index eba8ede8b..b4c91728d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,4 +12,4 @@ jobs: uses: multiversx/mx-sc-actions/.github/workflows/reproducible-build.yml@v4.2.2 with: image_tag: v7.0.0 - attach_to_existing_release: true + attach_to_existing_release: true \ No newline at end of file diff --git a/locked-asset/token-unstake/src/unbond_tokens.rs b/locked-asset/token-unstake/src/unbond_tokens.rs index 20aeb1843..74635fa2a 100644 --- a/locked-asset/token-unstake/src/unbond_tokens.rs +++ b/locked-asset/token-unstake/src/unbond_tokens.rs @@ -2,6 +2,8 @@ use crate::events; multiversx_sc::imports!(); +pub const MAX_CLAIM_UNLOCKED_TOKENS: u64 = 20; + #[multiversx_sc::module] pub trait UnbondTokensModule: crate::tokens_per_user::TokensPerUserModule @@ -16,9 +18,11 @@ pub trait UnbondTokensModule: let current_epoch = self.blockchain().get_block_epoch(); let mut output_payments = ManagedVec::new(); let mut penalty_tokens = ManagedVec::::new(); + let mut processed_count = 0; + self.unlocked_tokens_for_user(&caller) .update(|user_entries| { - while !user_entries.is_empty() { + while !user_entries.is_empty() && processed_count < MAX_CLAIM_UNLOCKED_TOKENS { let entry = user_entries.get(0); if current_epoch < entry.unlock_epoch { break; @@ -48,6 +52,8 @@ pub trait UnbondTokensModule: output_payments.push(unlocked_tokens); user_entries.remove(0); + + processed_count += 1; } }); diff --git a/locked-asset/token-unstake/tests/token_unstake_test.rs b/locked-asset/token-unstake/tests/token_unstake_test.rs index e247a4a0d..5627c6d1e 100644 --- a/locked-asset/token-unstake/tests/token_unstake_test.rs +++ b/locked-asset/token-unstake/tests/token_unstake_test.rs @@ -9,7 +9,10 @@ use multiversx_sc_scenario::{ use num_bigint::ToBigInt; use num_traits::cast::ToPrimitive; use simple_lock::locked_token::LockedTokenAttributes; -use token_unstake::tokens_per_user::{TokensPerUserModule, UnstakePair}; +use token_unstake::{ + tokens_per_user::{TokensPerUserModule, UnstakePair}, + unbond_tokens::MAX_CLAIM_UNLOCKED_TOKENS, +}; use token_unstake_setup::*; pub struct ResultWrapper @@ -99,6 +102,42 @@ fn cancel_unbond_test() { assert_eq!(user_energy, expected_energy); } +#[test] +fn unstake_multiple_position_test() { + let mut setup = + TokenUnstakeSetup::new(energy_factory::contract_obj, token_unstake::contract_obj); + let first_user = setup.first_user.clone(); + + let _ = setup.lock( + &first_user, + BASE_ASSET_TOKEN_ID, + USER_BALANCE, + LOCK_OPTIONS[2], + ); + + let number_of_unlocks = MAX_CLAIM_UNLOCKED_TOKENS * 2; + for _ in 0..number_of_unlocks { + let _ = setup.unlock_early(&first_user, 1, USER_BALANCE / number_of_unlocks); + } + + // unbond epochs pass + setup.b_mock.set_block_epoch(10 + UNBOND_EPOCHS); + // unbond ok + setup.unbond(&first_user).assert_ok(); + let balance_after_half_unlocks = rust_biguint!(USER_BALANCE as u128 / 2 * 2000 / 10000); // 80% fee on half balance + setup.b_mock.check_esdt_balance( + &first_user, + BASE_ASSET_TOKEN_ID, + &balance_after_half_unlocks, + ); + + setup.unbond(&first_user).assert_ok(); + let balance_after_all_unlocks = balance_after_half_unlocks * 2u64; + setup + .b_mock + .check_esdt_balance(&first_user, BASE_ASSET_TOKEN_ID, &balance_after_all_unlocks); +} + fn unbond_test_common( energy_factory_builder: EnergyFactoryBuilder, unstake_sc_builder: UnstakeScBuilder,