Skip to content

Commit

Permalink
feat: Refactor restake rewards (#712)
Browse files Browse the repository at this point in the history
  • Loading branch information
1xstj authored Jul 26, 2024
1 parent 5aada1a commit 17e41ee
Show file tree
Hide file tree
Showing 26 changed files with 44,648 additions and 29,691 deletions.
64 changes: 28 additions & 36 deletions pallets/multi-asset-delegation/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,44 +86,44 @@ benchmarks! {
}: _(RawOrigin::Signed(caller.clone()), additional_bond)
verify {
let operator = Operators::<T>::get(&caller).unwrap();
assert_eq!(operator.bond, bond_amount + additional_bond);
assert_eq!(operator.stake, bond_amount + additional_bond);
}

schedule_operator_bond_less {
schedule_operator_unstake {

let caller: T::AccountId = whitelisted_caller();
let bond_amount: BalanceOf<T> = T::Currency::minimum_balance() * 10u32.into();
MultiAssetDelegation::<T>::join_operators(RawOrigin::Signed(caller.clone()).into(), bond_amount)?;
let bond_less_amount: BalanceOf<T> = T::Currency::minimum_balance() * 5u32.into();
}: _(RawOrigin::Signed(caller.clone()), bond_less_amount)
let unstake_amount: BalanceOf<T> = T::Currency::minimum_balance() * 5u32.into();
}: _(RawOrigin::Signed(caller.clone()), unstake_amount)
verify {
let operator = Operators::<T>::get(&caller).unwrap();
let request = operator.request.unwrap();
assert_eq!(request.amount, bond_less_amount);
assert_eq!(request.amount, unstake_amount);
}

execute_operator_bond_less {
execute_operator_unstake {

let caller: T::AccountId = whitelisted_caller();
let bond_amount: BalanceOf<T> = T::Currency::minimum_balance() * 10u32.into();
MultiAssetDelegation::<T>::join_operators(RawOrigin::Signed(caller.clone()).into(), bond_amount)?;
let bond_less_amount: BalanceOf<T> = T::Currency::minimum_balance() * 5u32.into();
MultiAssetDelegation::<T>::schedule_operator_bond_less(RawOrigin::Signed(caller.clone()).into(), bond_less_amount)?;
let unstake_amount: BalanceOf<T> = T::Currency::minimum_balance() * 5u32.into();
MultiAssetDelegation::<T>::schedule_operator_unstake(RawOrigin::Signed(caller.clone()).into(), unstake_amount)?;
let current_round = Pallet::<T>::current_round();
CurrentRound::<T>::put(current_round + T::OperatorBondLessDelay::get());
}: _(RawOrigin::Signed(caller.clone()))
verify {
let operator = Operators::<T>::get(&caller).unwrap();
assert_eq!(operator.bond, bond_amount - bond_less_amount);
assert_eq!(operator.stake, bond_amount - unstake_amount);
}

cancel_operator_bond_less {
cancel_operator_unstake {

let caller: T::AccountId = whitelisted_caller();
let bond_amount: BalanceOf<T> = T::Currency::minimum_balance() * 10u32.into();
MultiAssetDelegation::<T>::join_operators(RawOrigin::Signed(caller.clone()).into(), bond_amount)?;
let bond_less_amount: BalanceOf<T> = T::Currency::minimum_balance() * 5u32.into();
MultiAssetDelegation::<T>::schedule_operator_bond_less(RawOrigin::Signed(caller.clone()).into(), bond_less_amount)?;
let unstake_amount: BalanceOf<T> = T::Currency::minimum_balance() * 5u32.into();
MultiAssetDelegation::<T>::schedule_operator_unstake(RawOrigin::Signed(caller.clone()).into(), unstake_amount)?;
}: _(RawOrigin::Signed(caller.clone()))
verify {
let operator = Operators::<T>::get(&caller).unwrap();
Expand Down Expand Up @@ -164,7 +164,7 @@ benchmarks! {
assert_eq!(metadata.deposits.get(&asset_id).unwrap(), &amount);
}

schedule_unstake {
schedule_withdraw {

let caller: T::AccountId = whitelisted_caller();
let asset_id: T::AssetId = 1_u32.into();
Expand All @@ -173,35 +173,35 @@ benchmarks! {
}: _(RawOrigin::Signed(caller.clone()), Some(asset_id), amount)
verify {
let metadata = Delegators::<T>::get(&caller).unwrap();
assert!(metadata.unstake_requests.is_some());
assert!(metadata.withdraw_requests.is_some());
}

execute_unstake {
execute_withdraw {

let caller: T::AccountId = whitelisted_caller();
let asset_id: T::AssetId = 1_u32.into();
let amount: BalanceOf<T> = T::Currency::minimum_balance() * 10u32.into();
MultiAssetDelegation::<T>::deposit(RawOrigin::Signed(caller.clone()).into(), Some(asset_id), amount)?;
MultiAssetDelegation::<T>::schedule_unstake(RawOrigin::Signed(caller.clone()).into(), Some(asset_id), amount)?;
MultiAssetDelegation::<T>::schedule_withdraw(RawOrigin::Signed(caller.clone()).into(), Some(asset_id), amount)?;
let current_round = Pallet::<T>::current_round();
CurrentRound::<T>::put(current_round + T::LeaveDelegatorsDelay::get());
}: _(RawOrigin::Signed(caller.clone()))
verify {
let metadata = Delegators::<T>::get(&caller).unwrap();
assert!(metadata.unstake_requests.is_none());
assert!(metadata.withdraw_requests.is_none());
}

cancel_unstake {
cancel_withdraw {

let caller: T::AccountId = whitelisted_caller();
let asset_id: T::AssetId = 1_u32.into();
let amount: BalanceOf<T> = T::Currency::minimum_balance() * 10u32.into();
MultiAssetDelegation::<T>::deposit(RawOrigin::Signed(caller.clone()).into(), Some(asset_id), amount)?;
MultiAssetDelegation::<T>::schedule_unstake(RawOrigin::Signed(caller.clone()).into(), Some(asset_id), amount)?;
MultiAssetDelegation::<T>::schedule_withdraw(RawOrigin::Signed(caller.clone()).into(), Some(asset_id), amount)?;
}: _(RawOrigin::Signed(caller.clone()))
verify {
let metadata = Delegators::<T>::get(&caller).unwrap();
assert!(metadata.unstake_requests.is_none());
assert!(metadata.withdraw_requests.is_none());
}

delegate {
Expand All @@ -219,7 +219,7 @@ benchmarks! {
assert_eq!(delegation.amount, amount);
}

schedule_delegator_bond_less {
schedule_delegator_unstake {

let caller: T::AccountId = whitelisted_caller();
let operator: T::AccountId = account("operator", 1, SEED);
Expand All @@ -231,10 +231,10 @@ benchmarks! {
}: _(RawOrigin::Signed(caller.clone()), operator.clone(), asset_id, amount)
verify {
let metadata = Delegators::<T>::get(&caller).unwrap();
assert!(metadata.delegator_bond_less_requests.is_some());
assert!(metadata.delegator_unstake_requests.is_some());
}

execute_delegator_bond_less {
execute_delegator_unstake {

let caller: T::AccountId = whitelisted_caller();
let operator: T::AccountId = account("operator", 1, SEED);
Expand All @@ -243,16 +243,16 @@ benchmarks! {
MultiAssetDelegation::<T>::join_operators(RawOrigin::Signed(operator.clone()).into(), T::Currency::minimum_balance() * 20u32.into())?;
MultiAssetDelegation::<T>::deposit(RawOrigin::Signed(caller.clone()).into(), Some(asset_id), amount)?;
MultiAssetDelegation::<T>::delegate(RawOrigin::Signed(caller.clone()).into(), operator.clone(), asset_id, amount)?;
MultiAssetDelegation::<T>::schedule_delegator_bond_less(RawOrigin::Signed(caller.clone()).into(), operator.clone(), asset_id, amount)?;
MultiAssetDelegation::<T>::schedule_delegator_unstake(RawOrigin::Signed(caller.clone()).into(), operator.clone(), asset_id, amount)?;
let current_round = Pallet::<T>::current_round();
CurrentRound::<T>::put(current_round + T::DelegationBondLessDelay::get());
}: _(RawOrigin::Signed(caller.clone()))
verify {
let metadata = Delegators::<T>::get(&caller).unwrap();
assert!(metadata.delegator_bond_less_requests.is_none());
assert!(metadata.delegator_unstake_requests.is_none());
}

cancel_delegator_bond_less {
cancel_delegator_unstake {

let caller: T::AccountId = whitelisted_caller();
let operator: T::AccountId = account("operator", 1, SEED);
Expand All @@ -261,19 +261,11 @@ benchmarks! {
MultiAssetDelegation::<T>::join_operators(RawOrigin::Signed(operator.clone()).into(), T::Currency::minimum_balance() * 20u32.into())?;
MultiAssetDelegation::<T>::deposit(RawOrigin::Signed(caller.clone()).into(), Some(asset_id), amount)?;
MultiAssetDelegation::<T>::delegate(RawOrigin::Signed(caller.clone()).into(), operator.clone(), asset_id, amount)?;
MultiAssetDelegation::<T>::schedule_delegator_bond_less(RawOrigin::Signed(caller.clone()).into(), operator.clone(), asset_id, amount)?;
MultiAssetDelegation::<T>::schedule_delegator_unstake(RawOrigin::Signed(caller.clone()).into(), operator.clone(), asset_id, amount)?;
}: _(RawOrigin::Signed(caller.clone()))
verify {
let metadata = Delegators::<T>::get(&caller).unwrap();
assert!(metadata.delegator_bond_less_requests.is_none());
}

set_whitelisted_assets {
let caller: T::AccountId = whitelisted_caller();
let assets: Vec<T::AssetId> = vec![1u32.into()];
}: _(RawOrigin::Root, assets.clone())
verify {
assert_eq!(WhitelistedAssets::<T>::get(), assets);
assert!(metadata.delegator_unstake_requests.is_none());
}

set_incentive_apy_and_cap {
Expand Down
49 changes: 23 additions & 26 deletions pallets/multi-asset-delegation/src/functions/delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ impl<T: Config> Pallet<T> {
})
}

/// Schedules a bond reduction for a delegator.
/// Schedules a stake reduction for a delegator.
///
/// # Arguments
///
Expand All @@ -116,8 +116,8 @@ impl<T: Config> Pallet<T> {
/// # Errors
///
/// Returns an error if the delegator has no active delegation,
/// or if the bond less amount is greater than the current delegation amount.
pub fn process_schedule_delegator_bond_less(
/// or if the unstake amount is greater than the current delegation amount.
pub fn process_schedule_delegator_unstake(
who: T::AccountId,
operator: T::AccountId,
asset_id: T::AssetId,
Expand All @@ -133,12 +133,12 @@ impl<T: Config> Pallet<T> {
.find(|d| d.operator == operator && d.asset_id == asset_id)
.ok_or(Error::<T>::NoActiveDelegation)?;

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

// Create the bond less request
// Create the unstake request
let current_round = Self::current_round();
metadata.delegator_bond_less_requests.push(BondLessRequest {
metadata.delegator_unstake_requests.push(BondLessRequest {
operator: delegation.operator.clone(),
asset_id,
amount,
Expand Down Expand Up @@ -177,31 +177,28 @@ impl<T: Config> Pallet<T> {
})
}

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

// Ensure there are outstanding bond less requests
ensure!(
!metadata.delegator_bond_less_requests.is_empty(),
Error::<T>::NoBondLessRequest
);
// Ensure there are outstanding unstake requests
ensure!(!metadata.delegator_unstake_requests.is_empty(), Error::<T>::NoBondLessRequest);

let current_round = Self::current_round();
let delay = T::DelegationBondLessDelay::get();

// Process all ready bond less requests
// Process all ready unstake requests
let mut executed_requests = Vec::new();
metadata.delegator_bond_less_requests.retain(|request| {
metadata.delegator_unstake_requests.retain(|request| {
if current_round >= delay + request.requested_round {
// Add the amount back to the delegator's deposits
metadata
Expand All @@ -223,37 +220,37 @@ impl<T: Config> Pallet<T> {
})
}

/// Cancels a scheduled bond reduction for a delegator.
/// Cancels a scheduled stake reduction for a delegator.
///
/// # 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.
/// * `asset_id` - The ID of the asset for which to cancel the unstake request.
/// * `amount` - The amount of the unstake request to cancel.
///
/// # Errors
///
/// 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(
/// Returns an error if the delegator has no matching unstake request or if there is no active delegation.
pub fn process_cancel_delegator_unstake(
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)?;

// Find and remove the matching bond less request
// Find and remove the matching unstake request
let request_index = metadata
.delegator_bond_less_requests
.delegator_unstake_requests
.iter()
.position(|r| r.asset_id == asset_id && r.amount == amount)
.ok_or(Error::<T>::NoBondLessRequest)?;

let bond_less_request = metadata.delegator_bond_less_requests.remove(request_index);
let unstake_request = metadata.delegator_unstake_requests.remove(request_index);

// Update the operator's metadata
Operators::<T>::try_mutate(
&bond_less_request.operator,
&unstake_request.operator,
|maybe_operator_metadata| -> DispatchResult {
let operator_metadata =
maybe_operator_metadata.as_mut().ok_or(Error::<T>::NotAnOperator)?;
Expand Down Expand Up @@ -282,7 +279,7 @@ impl<T: Config> Pallet<T> {

// Create a new delegation
metadata.delegations.push(BondInfoDelegator {
operator: bond_less_request.operator,
operator: unstake_request.operator,
amount,
asset_id,
});
Expand Down
Loading

0 comments on commit 17e41ee

Please sign in to comment.