Skip to content
This repository was archived by the owner on Apr 25, 2026. It is now read-only.

Commit 3842182

Browse files
authored
fix: update process_schedule_delegator_unstake (#1049)
* fix: update process_schedule_delegator_unstake * chore: format
1 parent 754f536 commit 3842182

2 files changed

Lines changed: 99 additions & 1 deletion

File tree

pallets/multi-asset-delegation/src/functions/delegate.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ impl<T: Config> Pallet<T> {
239239
let pending_unstake_amount: BalanceOf<T> = metadata
240240
.delegator_unstake_requests
241241
.iter()
242-
.filter(|r| r.operator == operator && r.asset == asset)
242+
.filter(|r| r.operator == operator && r.asset == asset && !r.is_nomination)
243243
.fold(Zero::zero(), |acc, r| acc.saturating_add(r.amount));
244244

245245
let available_amount = delegation.amount.saturating_sub(pending_unstake_amount);

pallets/multi-asset-delegation/src/tests/delegate.rs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -936,3 +936,101 @@ fn debug_tnt_delegation_verify_nomination_issue() {
936936
assert_eq!(delegation.amount, delegate_amount);
937937
});
938938
}
939+
940+
#[test]
941+
fn delegation_unstake_bug_with_nomination_pending() {
942+
// Test case that reproduces the bug where delegation unstake calculation
943+
// incorrectly includes nomination unstake requests
944+
new_test_ext().execute_with(|| {
945+
let delegator: AccountId = Bob.into();
946+
let operator: AccountId = Alice.into();
947+
// Use the same asset that nominations use: Asset::Custom(Zero::zero()) which is
948+
// Asset::Custom(0)
949+
let asset = Asset::Custom(0);
950+
let delegation_amount = 500;
951+
let nomination_amount = 300;
952+
let _delegation_unstake_amount = 200;
953+
let nomination_unstake_amount = 100;
954+
955+
// Setup operator
956+
assert_ok!(MultiAssetDelegation::join_operators(
957+
RuntimeOrigin::signed(operator.clone()),
958+
10_000
959+
));
960+
961+
// Create tokens and deposits for delegator
962+
create_and_mint_tokens(0, delegator.clone(), delegation_amount);
963+
assert_ok!(MultiAssetDelegation::deposit(
964+
RuntimeOrigin::signed(delegator.clone()),
965+
asset.clone(),
966+
delegation_amount,
967+
None,
968+
None,
969+
));
970+
971+
// Create a regular delegation
972+
assert_ok!(MultiAssetDelegation::delegate(
973+
RuntimeOrigin::signed(delegator.clone()),
974+
operator.clone(),
975+
asset.clone(),
976+
delegation_amount,
977+
Default::default(),
978+
));
979+
980+
// Setup nomination for native restaking
981+
assert_ok!(Staking::bond(
982+
RuntimeOrigin::signed(delegator.clone()),
983+
nomination_amount,
984+
pallet_staking::RewardDestination::Staked
985+
));
986+
assert_ok!(Staking::nominate(RuntimeOrigin::signed(delegator.clone()), vec![
987+
operator.clone()
988+
]));
989+
990+
// Create nomination delegation (simulate native restaking)
991+
assert_ok!(MultiAssetDelegation::delegate_nomination(
992+
RuntimeOrigin::signed(delegator.clone()),
993+
operator.clone(),
994+
nomination_amount,
995+
Default::default(),
996+
));
997+
998+
// Schedule nomination unstake first
999+
assert_ok!(MultiAssetDelegation::schedule_nomination_unstake(
1000+
RuntimeOrigin::signed(delegator.clone()),
1001+
operator.clone(),
1002+
nomination_unstake_amount,
1003+
Default::default(),
1004+
));
1005+
1006+
// Verify nomination unstake request exists
1007+
let metadata = MultiAssetDelegation::delegators(delegator.clone()).unwrap();
1008+
assert_eq!(metadata.delegator_unstake_requests.len(), 1);
1009+
let nomination_request = &metadata.delegator_unstake_requests[0];
1010+
assert!(nomination_request.is_nomination);
1011+
assert_eq!(nomination_request.amount, nomination_unstake_amount);
1012+
1013+
// Now try to schedule regular delegation unstake for the full amount
1014+
// This should succeed as we have 500 - 0 = 500 available for delegation
1015+
assert_ok!(MultiAssetDelegation::schedule_delegator_unstake(
1016+
RuntimeOrigin::signed(delegator.clone()),
1017+
operator.clone(),
1018+
asset.clone(),
1019+
delegation_amount,
1020+
));
1021+
1022+
// Verify both unstake requests exist
1023+
let metadata = MultiAssetDelegation::delegators(delegator.clone()).unwrap();
1024+
assert_eq!(metadata.delegator_unstake_requests.len(), 2);
1025+
1026+
// Check first request is nomination
1027+
let first_request = &metadata.delegator_unstake_requests[0];
1028+
assert!(first_request.is_nomination);
1029+
assert_eq!(first_request.amount, nomination_unstake_amount);
1030+
1031+
// Check second request is delegation
1032+
let second_request = &metadata.delegator_unstake_requests[1];
1033+
assert!(!second_request.is_nomination);
1034+
assert_eq!(second_request.amount, delegation_amount);
1035+
});
1036+
}

0 commit comments

Comments
 (0)