Skip to content

Commit

Permalink
slash behavior on pallet vesting
Browse files Browse the repository at this point in the history
  • Loading branch information
JuaniRios committed Sep 4, 2024
1 parent bc33597 commit a77032a
Show file tree
Hide file tree
Showing 5 changed files with 255 additions and 0 deletions.
16 changes: 16 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 35 additions & 0 deletions pallets/on-slash-vesting/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[package]
name = "on-slash-vesting"
authors.workspace = true
documentation.workspace = true
edition.workspace = true
homepage.workspace = true
license-file.workspace = true
readme.workspace = true
repository.workspace = true
version.workspace = true

[dependencies]
pallet-vesting.workspace = true
frame-support.workspace = true
frame-system.workspace = true
pallet-balances.workspace = true
log.workspace = true
parity-scale-codec.workspace = true
scale-info.workspace = true
sp-runtime.workspace = true
sp-io.workspace = true
serde = { version = "1.0.204", features = ["derive"] }
[lints]
workspace = true


[features]
default = [ "std" ]

std = [
"frame-support/std",
"frame-system/std",
"pallet-balances/std",
"pallet-vesting/std",
]
50 changes: 50 additions & 0 deletions pallets/on-slash-vesting/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Ensure we're `no_std` when compiling for Wasm.
#![cfg_attr(not(feature = "std"), no_std)]

#[cfg(test)]
mod mock;
#[cfg(test)]
mod test;

use frame_support::{
sp_runtime::{traits::Convert, FixedPointNumber, FixedU128},
traits::{Currency, OriginTrait},
};
use pallet_vesting::Vesting;
use sp_runtime::traits::BlockNumberProvider;

trait OnSlash<AccountId, Balance> {
fn on_slash(account: &AccountId, amount: Balance);
}
type AccountIdOf<T> = <T as frame_system::Config>::AccountId;
impl<T> OnSlash<AccountIdOf<T>, u128> for pallet_vesting::Pallet<T>
where
T: pallet_vesting::Config,
T::Currency: Currency<AccountIdOf<T>, Balance = u128>,
{
fn on_slash(account: &AccountIdOf<T>, slashed_amount: u128) {
let Some(vesting_schedules) = <Vesting<T>>::get(account) else { return };
let vesting_schedules = vesting_schedules.to_vec();
let mut new_vesting_schedules = Vec::new();
let now = T::BlockNumberProvider::current_block_number();
for schedule in vesting_schedules {
let total_locked = schedule.locked_at::<T::BlockNumberToBalance>(now).saturating_sub(slashed_amount);
let start_block: u128 = T::BlockNumberToBalance::convert(now);
let end_block: u128 = schedule.ending_block_as_balance::<T::BlockNumberToBalance>();
let duration = end_block.saturating_sub(start_block);
let per_block = FixedU128::from_rational(total_locked, duration).saturating_mul_int(1u128);
let new_schedule = pallet_vesting::VestingInfo::new(total_locked, per_block, now);
if new_schedule.is_valid() {
new_vesting_schedules.push(new_schedule);
}
}
dbg!(&new_vesting_schedules);
let Ok(new_vesting_schedules) = new_vesting_schedules.try_into() else {
log::error!("Failed to convert new vesting schedules into BoundedVec");
return
};
<Vesting<T>>::set(account, Some(new_vesting_schedules));
let vest_result = <pallet_vesting::Pallet<T>>::vest(T::RuntimeOrigin::signed(account.clone()));
debug_assert!(vest_result.is_ok());
}
}
109 changes: 109 additions & 0 deletions pallets/on-slash-vesting/src/mock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
use frame_support::traits::VariantCount;
use frame_support::{
derive_impl, parameter_types,
sp_runtime::{traits::IdentityLookup, BuildStorage},
traits::WithdrawReasons,
};
use frame_support::__private::RuntimeDebug;
use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
use sp_runtime::traits::{ConvertInto, Identity};
use serde::{Deserialize, Serialize};

frame_support::construct_runtime!(
pub enum TestRuntime {
System: frame_system = 0,
Balances: pallet_balances = 1,
Vesting: pallet_vesting = 2,
}
);
type Block = frame_system::mocking::MockBlock<TestRuntime>;

#[derive(
Encode,
Decode,
Copy,
Clone,
PartialEq,
Eq,
RuntimeDebug,
MaxEncodedLen,
TypeInfo,
Ord,
PartialOrd,
Serialize,
Deserialize,
)]
pub enum MockRuntimeHoldReason {
Reason,
Reason2,
}
impl VariantCount for MockRuntimeHoldReason {
const VARIANT_COUNT: u32 = 2;
}

#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
impl frame_system::Config for TestRuntime {
type AccountData = pallet_balances::AccountData<u128>;
type AccountId = u64;
type Block = Block;
type Lookup = IdentityLookup<Self::AccountId>;
}

parameter_types! {
pub const MinVestedTransfer: u64 = 10;
pub UnvestedFundsAllowedWithdrawReasons: WithdrawReasons =
WithdrawReasons::except(WithdrawReasons::TRANSFER | WithdrawReasons::RESERVE);
pub static ExistentialDeposit: u128 = 10u128.pow(7);
}

#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)]
impl pallet_balances::Config for TestRuntime {
type AccountStore = System;
type ExistentialDeposit = ExistentialDeposit;
type Balance = u128;
type RuntimeHoldReason = MockRuntimeHoldReason;
}

impl pallet_vesting::Config for TestRuntime {
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkReason = BenchmarkReason;
type BlockNumberProvider = System;
type BlockNumberToBalance = ConvertInto;
type Currency = Balances;
type MinVestedTransfer = MinVestedTransfer;
type RuntimeEvent = RuntimeEvent;
type UnvestedFundsAllowedWithdrawReasons = UnvestedFundsAllowedWithdrawReasons;
type WeightInfo = ();

const MAX_VESTING_SCHEDULES: u32 = 4;
}

#[derive(Default)]
pub struct ExtBuilder {
pub existential_deposit: u128,
}

impl ExtBuilder {
pub fn existential_deposit(mut self, existential_deposit: u128) -> Self {
self.existential_deposit = existential_deposit;
self
}

pub fn build(self) -> sp_io::TestExternalities {
EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit);
let mut t = frame_system::GenesisConfig::<TestRuntime>::default().build_storage().unwrap();
pallet_balances::GenesisConfig::<TestRuntime> {
balances: vec![
(1, self.existential_deposit),
(2, self.existential_deposit),
(3, self.existential_deposit),
(4, self.existential_deposit),
],
}
.assimilate_storage(&mut t)
.unwrap();

sp_io::TestExternalities::new(t)
}
}
45 changes: 45 additions & 0 deletions pallets/on-slash-vesting/src/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
extern crate alloc;
use super::{mock::*, *};
use mock::Vesting as PalletVesting;
use mock::Balances as PalletBalances;
use mock::System as PalletSystem;
use frame_support::traits::tokens::fungible::Mutate;
use frame_support::traits::tokens::fungible::Inspect;
use frame_support::traits::tokens::fungible::BalancedHold;
use frame_support::traits::tokens::fungible::MutateHold;
use frame_support::assert_ok;
use pallet_vesting::VestingInfo;

#[test]
fn sandbox() {
ExtBuilder { existential_deposit: 1 }.build().execute_with(|| {
<PalletBalances as Mutate<u64>>::set_balance(&1, 0);
<PalletBalances as Mutate<u64>>::set_balance(&2, 100);
let vesting_info = VestingInfo::new(100, 10, 1);
assert_ok!(PalletVesting::vested_transfer(RuntimeOrigin::signed(2), 1, vesting_info));
assert_ok!(<PalletBalances as MutateHold<u64>>::hold(&MockRuntimeHoldReason::Reason, &1u64, 30u128));

assert_eq!(PalletBalances::usable_balance(1), 0);

PalletSystem::set_block_number(3);
// Unlock 20
assert_ok!(PalletVesting::vest(RuntimeOrigin::signed(1)));
assert_eq!(PalletBalances::usable_balance(1), 20);
dbg!(<pallet_vesting::Vesting<TestRuntime>>::get(1));

// Slash 30
<PalletBalances as BalancedHold<u64>>::slash(&MockRuntimeHoldReason::Reason, &1u64, 30u128);
<PalletVesting as OnSlash<u64, u128>>::on_slash(&1, 30);
// It is actually -10
dbg!(PalletBalances::usable_balance(1), 0);

// After calling on_slash, the previously unlocked 20 should be available again
let account_data = PalletSystem::account(&1).data;
dbg!(account_data);
assert_eq!(PalletBalances::usable_balance(1), 20);




});
}

0 comments on commit a77032a

Please sign in to comment.