-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
255 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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", | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
|
||
|
||
|
||
|
||
}); | ||
} |