Skip to content

Commit

Permalink
feat: Allow multiple unstake/bond_less requests (#708)
Browse files Browse the repository at this point in the history
  • Loading branch information
1xstj authored Jul 26, 2024
1 parent 120018b commit 5aada1a
Show file tree
Hide file tree
Showing 7 changed files with 341 additions and 196 deletions.
12 changes: 6 additions & 6 deletions pallets/multi-asset-delegation/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ benchmarks! {
}: _(RawOrigin::Signed(caller.clone()), Some(asset_id), amount)
verify {
let metadata = Delegators::<T>::get(&caller).unwrap();
assert!(metadata.unstake_request.is_some());
assert!(metadata.unstake_requests.is_some());
}

execute_unstake {
Expand All @@ -188,7 +188,7 @@ benchmarks! {
}: _(RawOrigin::Signed(caller.clone()))
verify {
let metadata = Delegators::<T>::get(&caller).unwrap();
assert!(metadata.unstake_request.is_none());
assert!(metadata.unstake_requests.is_none());
}

cancel_unstake {
Expand All @@ -201,7 +201,7 @@ benchmarks! {
}: _(RawOrigin::Signed(caller.clone()))
verify {
let metadata = Delegators::<T>::get(&caller).unwrap();
assert!(metadata.unstake_request.is_none());
assert!(metadata.unstake_requests.is_none());
}

delegate {
Expand Down Expand Up @@ -231,7 +231,7 @@ benchmarks! {
}: _(RawOrigin::Signed(caller.clone()), operator.clone(), asset_id, amount)
verify {
let metadata = Delegators::<T>::get(&caller).unwrap();
assert!(metadata.delegator_bond_less_request.is_some());
assert!(metadata.delegator_bond_less_requests.is_some());
}

execute_delegator_bond_less {
Expand All @@ -249,7 +249,7 @@ benchmarks! {
}: _(RawOrigin::Signed(caller.clone()))
verify {
let metadata = Delegators::<T>::get(&caller).unwrap();
assert!(metadata.delegator_bond_less_request.is_none());
assert!(metadata.delegator_bond_less_requests.is_none());
}

cancel_delegator_bond_less {
Expand All @@ -265,7 +265,7 @@ benchmarks! {
}: _(RawOrigin::Signed(caller.clone()))
verify {
let metadata = Delegators::<T>::get(&caller).unwrap();
assert!(metadata.delegator_bond_less_request.is_none());
assert!(metadata.delegator_bond_less_requests.is_none());
}

set_whitelisted_assets {
Expand Down
192 changes: 111 additions & 81 deletions pallets/multi-asset-delegation/src/functions/delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ use crate::Pallet;
use frame_support::ensure;
use frame_support::pallet_prelude::DispatchResult;
use frame_support::traits::Get;

use sp_runtime::traits::Zero;
use sp_std::vec::Vec;

impl<T: Config> Pallet<T> {
/// Processes the delegation of an amount of an asset to an operator.
Expand Down Expand Up @@ -57,12 +57,20 @@ impl<T: Config> Pallet<T> {
metadata.deposits.remove(&asset_id);
}

// Create a new delegation
metadata.delegations.push(BondInfoDelegator {
operator: operator.clone(),
amount,
asset_id,
});
// Check if the delegation exists and update it, otherwise create a new delegation
if let Some(delegation) = metadata
.delegations
.iter_mut()
.find(|d| d.operator == operator && d.asset_id == asset_id)
{
delegation.amount += amount;
} else {
metadata.delegations.push(BondInfoDelegator {
operator: operator.clone(),
amount,
asset_id,
});
}

// Update the status
metadata.status = DelegatorStatus::Active;
Expand All @@ -72,15 +80,22 @@ impl<T: Config> Pallet<T> {
let operator_metadata =
maybe_operator_metadata.as_mut().ok_or(Error::<T>::NotAnOperator)?;

// Increase the delegation count
operator_metadata.delegation_count += 1;

// Add the new delegation
operator_metadata.delegations.push(DelegatorBond {
delegator: who.clone(),
amount,
asset_id,
});
// Check if the delegation exists and update it, otherwise create a new delegation
if let Some(delegation) = operator_metadata
.delegations
.iter_mut()
.find(|d| d.delegator == who && d.asset_id == asset_id)
{
delegation.amount += amount;
} else {
operator_metadata.delegations.push(DelegatorBond {
delegator: who.clone(),
amount,
asset_id,
});
// Increase the delegation count only when a new delegation is added
operator_metadata.delegation_count += 1;
}

Ok(())
})?;
Expand All @@ -101,7 +116,7 @@ impl<T: Config> Pallet<T> {
/// # Errors
///
/// Returns an error if the delegator has no active delegation,
/// if there is an existing bond less request, or if the bond less amount is greater than the current delegation amount.
/// or if the bond less amount is greater than the current delegation amount.
pub fn process_schedule_delegator_bond_less(
who: T::AccountId,
operator: T::AccountId,
Expand All @@ -118,19 +133,17 @@ impl<T: Config> Pallet<T> {
.find(|d| d.operator == operator && d.asset_id == asset_id)
.ok_or(Error::<T>::NoActiveDelegation)?;

// Ensure there is no outstanding bond less request
ensure!(
metadata.delegator_bond_less_request.is_none(),
Error::<T>::BondLessRequestAlreadyExists
);

// Ensure the amount to bond less is not greater than the current delegation amount
ensure!(delegation.amount >= amount, Error::<T>::InsufficientBalance);

// Create the bond less request
let current_round = Self::current_round();
metadata.delegator_bond_less_request =
Some(BondLessRequest { asset_id, amount, requested_round: current_round });
metadata.delegator_bond_less_requests.push(BondLessRequest {
operator: delegation.operator.clone(),
asset_id,
amount,
requested_round: current_round,
});

// Update the operator's metadata
Operators::<T>::try_mutate(&operator, |maybe_operator_metadata| -> DispatchResult {
Expand Down Expand Up @@ -164,41 +177,47 @@ impl<T: Config> Pallet<T> {
})
}

/// Executes a scheduled bond reduction for a delegator.
/// Executes scheduled bond reductions for a delegator.
///
/// # Arguments
///
/// * `who` - The account ID of the delegator.
///
/// # Errors
///
/// Returns an error if the delegator has no bond less request or if the bond less request is not ready.
/// Returns an error if the delegator has no bond less requests or if none of the bond less requests are ready.
pub fn process_execute_delegator_bond_less(who: T::AccountId) -> DispatchResult {
Delegators::<T>::try_mutate(&who, |maybe_metadata| {
let metadata = maybe_metadata.as_mut().ok_or(Error::<T>::NotDelegator)?;

// Ensure there is an outstanding bond less request
let bond_less_request = metadata
.delegator_bond_less_request
.as_ref()
.ok_or(Error::<T>::NoBondLessRequest)?;

// Check if the requested round has been reached
// Ensure there are outstanding bond less requests
ensure!(
Self::current_round()
>= T::DelegationBondLessDelay::get() + bond_less_request.requested_round,
Error::<T>::BondLessNotReady
!metadata.delegator_bond_less_requests.is_empty(),
Error::<T>::NoBondLessRequest
);

// Get the asset ID and amount from the bond less request
let asset_id = bond_less_request.asset_id;
let amount = bond_less_request.amount;

// Add the amount back to the delegator's deposits
metadata.deposits.entry(asset_id).and_modify(|e| *e += amount).or_insert(amount);
let current_round = Self::current_round();
let delay = T::DelegationBondLessDelay::get();

// Process all ready bond less requests
let mut executed_requests = Vec::new();
metadata.delegator_bond_less_requests.retain(|request| {
if current_round >= delay + request.requested_round {
// Add the amount back to the delegator's deposits
metadata
.deposits
.entry(request.asset_id)
.and_modify(|e| *e += request.amount)
.or_insert(request.amount);
executed_requests.push(request.clone());
false // Remove this request
} else {
true // Keep this request
}
});

// Clear the bond less request
metadata.delegator_bond_less_request = None;
// If no requests were executed, return an error
ensure!(!executed_requests.is_empty(), Error::<T>::BondLessNotReady);

Ok(())
})
Expand All @@ -209,53 +228,64 @@ impl<T: Config> Pallet<T> {
/// # Arguments
///
/// * `who` - The account ID of the delegator.
/// * `asset_id` - The ID of the asset for which to cancel the bond less request.
/// * `amount` - The amount of the bond less request to cancel.
///
/// # Errors
///
/// Returns an error if the delegator has no bond less request or if there is no active delegation.
pub fn process_cancel_delegator_bond_less(who: T::AccountId) -> DispatchResult {
/// Returns an error if the delegator has no matching bond less request or if there is no active delegation.
pub fn process_cancel_delegator_bond_less(
who: T::AccountId,
asset_id: T::AssetId,
amount: BalanceOf<T>,
) -> DispatchResult {
Delegators::<T>::try_mutate(&who, |maybe_metadata| {
let metadata = maybe_metadata.as_mut().ok_or(Error::<T>::NotDelegator)?;

// Ensure there is an outstanding bond less request
let bond_less_request = metadata
.delegator_bond_less_request
.take()
.ok_or(Error::<T>::NoBondLessRequest)?;

// Get the asset ID and amount from the bond less request
let asset_id = bond_less_request.asset_id;
let amount = bond_less_request.amount;

// Find the operator associated with the bond less request
let operator = metadata
.delegations
// Find and remove the matching bond less request
let request_index = metadata
.delegator_bond_less_requests
.iter()
.find(|d| d.asset_id == asset_id && d.amount >= amount)
.ok_or(Error::<T>::NoActiveDelegation)?
.operator
.clone();
.position(|r| r.asset_id == asset_id && r.amount == amount)
.ok_or(Error::<T>::NoBondLessRequest)?;

// Add the amount back to the delegator's deposits
metadata.deposits.entry(asset_id).and_modify(|e| *e += amount).or_insert(amount);
let bond_less_request = metadata.delegator_bond_less_requests.remove(request_index);

// Update the operator's metadata
Operators::<T>::try_mutate(&operator, |maybe_operator_metadata| -> DispatchResult {
let operator_metadata =
maybe_operator_metadata.as_mut().ok_or(Error::<T>::NotAnOperator)?;

// Increase the delegation count
operator_metadata.delegation_count += 1;
Operators::<T>::try_mutate(
&bond_less_request.operator,
|maybe_operator_metadata| -> DispatchResult {
let operator_metadata =
maybe_operator_metadata.as_mut().ok_or(Error::<T>::NotAnOperator)?;

// Find the matching delegation and increase its amount, or insert a new delegation if not found
if let Some(delegation) = operator_metadata
.delegations
.iter_mut()
.find(|d| d.asset_id == asset_id && d.delegator == who.clone())
{
delegation.amount += amount;
} else {
operator_metadata.delegations.push(DelegatorBond {
delegator: who.clone(),
amount,
asset_id,
});

// Increase the delegation count
operator_metadata.delegation_count += 1;
}

Ok(())
},
)?;

// Add the new delegation
operator_metadata.delegations.push(DelegatorBond {
delegator: who.clone(),
amount,
asset_id,
});

Ok(())
})?;
// Create a new delegation
metadata.delegations.push(BondInfoDelegator {
operator: bond_less_request.operator,
amount,
asset_id,
});

Ok(())
})
Expand Down
Loading

0 comments on commit 5aada1a

Please sign in to comment.