mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 14:01:06 +00:00
Adds vested_transfer to Vesting pallet (#5029)
* Add the vested_transfer function with tests * Add VestingDeposit for minimum amount to create a new schedule * Remove irrelevant file commit * Bump spec * Add weight comment * Fix CI build * Make the check before the transfer * Update frame/vesting/src/lib.rs Co-Authored-By: Shawn Tabrizi <shawntabrizi@gmail.com> * Update frame/vesting/src/lib.rs Co-Authored-By: Shawn Tabrizi <shawntabrizi@gmail.com> * Update frame/vesting/src/lib.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update frame/vesting/src/lib.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update frame/vesting/src/lib.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Add tab to line 249 * Rename to `MinVestedTransfer` for clarity Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
This commit is contained in:
@@ -82,8 +82,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
|
|||||||
// and set impl_version to 0. If only runtime
|
// and set impl_version to 0. If only runtime
|
||||||
// implementation changes and behavior does not, then leave spec_version as
|
// implementation changes and behavior does not, then leave spec_version as
|
||||||
// is and increment impl_version.
|
// is and increment impl_version.
|
||||||
spec_version: 227,
|
spec_version: 229,
|
||||||
impl_version: 1,
|
impl_version: 0,
|
||||||
apis: RUNTIME_API_VERSIONS,
|
apis: RUNTIME_API_VERSIONS,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -590,10 +590,15 @@ impl pallet_society::Trait for Runtime {
|
|||||||
type ChallengePeriod = ChallengePeriod;
|
type ChallengePeriod = ChallengePeriod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parameter_types! {
|
||||||
|
pub const MinVestedTransfer: Balance = 100 * DOLLARS;
|
||||||
|
}
|
||||||
|
|
||||||
impl pallet_vesting::Trait for Runtime {
|
impl pallet_vesting::Trait for Runtime {
|
||||||
type Event = Event;
|
type Event = Event;
|
||||||
type Currency = Balances;
|
type Currency = Balances;
|
||||||
type BlockNumberToBalance = ConvertInto;
|
type BlockNumberToBalance = ConvertInto;
|
||||||
|
type MinVestedTransfer = MinVestedTransfer;
|
||||||
}
|
}
|
||||||
|
|
||||||
construct_runtime!(
|
construct_runtime!(
|
||||||
|
|||||||
@@ -52,10 +52,12 @@ use codec::{Encode, Decode};
|
|||||||
use sp_runtime::{DispatchResult, RuntimeDebug, traits::{
|
use sp_runtime::{DispatchResult, RuntimeDebug, traits::{
|
||||||
StaticLookup, Zero, AtLeast32Bit, MaybeSerializeDeserialize, Convert
|
StaticLookup, Zero, AtLeast32Bit, MaybeSerializeDeserialize, Convert
|
||||||
}};
|
}};
|
||||||
use frame_support::{decl_module, decl_event, decl_storage, decl_error};
|
use frame_support::{decl_module, decl_event, decl_storage, decl_error, ensure};
|
||||||
use frame_support::traits::{
|
use frame_support::traits::{
|
||||||
Currency, LockableCurrency, VestingSchedule, WithdrawReason, LockIdentifier
|
Currency, LockableCurrency, VestingSchedule, WithdrawReason, LockIdentifier, ExistenceRequirement,
|
||||||
|
Get,
|
||||||
};
|
};
|
||||||
|
use frame_support::weights::SimpleDispatchInfo;
|
||||||
use frame_system::{self as system, ensure_signed};
|
use frame_system::{self as system, ensure_signed};
|
||||||
|
|
||||||
type BalanceOf<T> = <<T as Trait>::Currency as Currency<<T as frame_system::Trait>::AccountId>>::Balance;
|
type BalanceOf<T> = <<T as Trait>::Currency as Currency<<T as frame_system::Trait>::AccountId>>::Balance;
|
||||||
@@ -69,6 +71,9 @@ pub trait Trait: frame_system::Trait {
|
|||||||
|
|
||||||
/// Convert the block number into a balance.
|
/// Convert the block number into a balance.
|
||||||
type BlockNumberToBalance: Convert<Self::BlockNumber, BalanceOf<Self>>;
|
type BlockNumberToBalance: Convert<Self::BlockNumber, BalanceOf<Self>>;
|
||||||
|
|
||||||
|
/// The minimum amount transferred to call `vested_transfer`.
|
||||||
|
type MinVestedTransfer: Get<BalanceOf<Self>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const VESTING_ID: LockIdentifier = *b"vesting ";
|
const VESTING_ID: LockIdentifier = *b"vesting ";
|
||||||
@@ -158,6 +163,8 @@ decl_error! {
|
|||||||
NotVesting,
|
NotVesting,
|
||||||
/// An existing vesting schedule already exists for this account that cannot be clobbered.
|
/// An existing vesting schedule already exists for this account that cannot be clobbered.
|
||||||
ExistingVestingSchedule,
|
ExistingVestingSchedule,
|
||||||
|
/// Amount being transferred is too low to create a vesting schedule.
|
||||||
|
AmountLow,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,6 +173,9 @@ decl_module! {
|
|||||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||||
type Error = Error<T>;
|
type Error = Error<T>;
|
||||||
|
|
||||||
|
/// The minimum amount to be transferred to create a new vesting schedule.
|
||||||
|
const MinVestedTransfer: BalanceOf<T> = T::MinVestedTransfer::get();
|
||||||
|
|
||||||
fn deposit_event() = default;
|
fn deposit_event() = default;
|
||||||
|
|
||||||
/// Unlock any vested funds of the sender account.
|
/// Unlock any vested funds of the sender account.
|
||||||
@@ -206,6 +216,40 @@ decl_module! {
|
|||||||
ensure_signed(origin)?;
|
ensure_signed(origin)?;
|
||||||
Self::update_lock(T::Lookup::lookup(target)?)
|
Self::update_lock(T::Lookup::lookup(target)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a vested transfer.
|
||||||
|
///
|
||||||
|
/// The dispatch origin for this call must be _Signed_.
|
||||||
|
///
|
||||||
|
/// - `target`: The account that should be transferred the vested funds.
|
||||||
|
/// - `amount`: The amount of funds to transfer and will be vested.
|
||||||
|
/// - `schedule`: The vesting schedule attached to the transfer.
|
||||||
|
///
|
||||||
|
/// Emits `VestingCreated`.
|
||||||
|
///
|
||||||
|
/// # <weight>
|
||||||
|
/// - Creates a new storage entry, but is protected by a minimum transfer
|
||||||
|
/// amount needed to succeed.
|
||||||
|
/// # </weight>
|
||||||
|
#[weight = SimpleDispatchInfo::FixedNormal(1_000_000)]
|
||||||
|
pub fn vested_transfer(
|
||||||
|
origin,
|
||||||
|
target: <T::Lookup as StaticLookup>::Source,
|
||||||
|
schedule: VestingInfo<BalanceOf<T>, T::BlockNumber>,
|
||||||
|
) -> DispatchResult {
|
||||||
|
let transactor = ensure_signed(origin)?;
|
||||||
|
ensure!(schedule.locked >= T::MinVestedTransfer::get(), Error::<T>::AmountLow);
|
||||||
|
|
||||||
|
let who = T::Lookup::lookup(target)?;
|
||||||
|
ensure!(!Vesting::<T>::contains_key(&who), Error::<T>::ExistingVestingSchedule);
|
||||||
|
|
||||||
|
T::Currency::transfer(&transactor, &who, schedule.locked, ExistenceRequirement::AllowDeath)?;
|
||||||
|
|
||||||
|
Self::add_vesting_schedule(&who, schedule.locked, schedule.per_block, schedule.starting_block)
|
||||||
|
.expect("user does not have an existing vesting schedule; q.e.d.");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -348,10 +392,14 @@ mod tests {
|
|||||||
type ExistentialDeposit = ExistentialDeposit;
|
type ExistentialDeposit = ExistentialDeposit;
|
||||||
type AccountStore = System;
|
type AccountStore = System;
|
||||||
}
|
}
|
||||||
|
parameter_types! {
|
||||||
|
pub const MinVestedTransfer: u64 = 256 * 2;
|
||||||
|
}
|
||||||
impl Trait for Test {
|
impl Trait for Test {
|
||||||
type Event = ();
|
type Event = ();
|
||||||
type Currency = Balances;
|
type Currency = Balances;
|
||||||
type BlockNumberToBalance = Identity;
|
type BlockNumberToBalance = Identity;
|
||||||
|
type MinVestedTransfer = MinVestedTransfer;
|
||||||
}
|
}
|
||||||
type System = frame_system::Module<Test>;
|
type System = frame_system::Module<Test>;
|
||||||
type Balances = pallet_balances::Module<Test>;
|
type Balances = pallet_balances::Module<Test>;
|
||||||
@@ -613,4 +661,95 @@ mod tests {
|
|||||||
assert_ok!(Balances::transfer(Some(12).into(), 3, 256 * 5));
|
assert_ok!(Balances::transfer(Some(12).into(), 3, 256 * 5));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vested_transfer_works() {
|
||||||
|
ExtBuilder::default()
|
||||||
|
.existential_deposit(256)
|
||||||
|
.build()
|
||||||
|
.execute_with(|| {
|
||||||
|
assert_eq!(System::block_number(), 1);
|
||||||
|
let user3_free_balance = Balances::free_balance(&3);
|
||||||
|
let user4_free_balance = Balances::free_balance(&4);
|
||||||
|
assert_eq!(user3_free_balance, 256 * 30);
|
||||||
|
assert_eq!(user4_free_balance, 256 * 40);
|
||||||
|
// Account 4 should not have any vesting yet.
|
||||||
|
assert_eq!(Vesting::vesting(&4), None);
|
||||||
|
// Make the schedule for the new transfer.
|
||||||
|
let new_vesting_schedule = VestingInfo {
|
||||||
|
locked: 256 * 5,
|
||||||
|
per_block: 64, // Vesting over 20 blocks
|
||||||
|
starting_block: 10,
|
||||||
|
};
|
||||||
|
assert_ok!(Vesting::vested_transfer(Some(3).into(), 4, new_vesting_schedule));
|
||||||
|
// Now account 4 should have vesting.
|
||||||
|
assert_eq!(Vesting::vesting(&4), Some(new_vesting_schedule));
|
||||||
|
// Ensure the transfer happened correctly.
|
||||||
|
let user3_free_balance_updated = Balances::free_balance(&3);
|
||||||
|
assert_eq!(user3_free_balance_updated, 256 * 25);
|
||||||
|
let user4_free_balance_updated = Balances::free_balance(&4);
|
||||||
|
assert_eq!(user4_free_balance_updated, 256 * 45);
|
||||||
|
// Account 4 has 5 * 256 locked.
|
||||||
|
assert_eq!(Vesting::vesting_balance(&4), Some(256 * 5));
|
||||||
|
|
||||||
|
System::set_block_number(20);
|
||||||
|
assert_eq!(System::block_number(), 20);
|
||||||
|
|
||||||
|
// Account 4 has 5 * 64 units vested by block 20.
|
||||||
|
assert_eq!(Vesting::vesting_balance(&4), Some(10 * 64));
|
||||||
|
|
||||||
|
System::set_block_number(30);
|
||||||
|
assert_eq!(System::block_number(), 30);
|
||||||
|
|
||||||
|
// Account 4 has fully vested.
|
||||||
|
assert_eq!(Vesting::vesting_balance(&4), Some(0));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vested_transfer_correctly_fails() {
|
||||||
|
ExtBuilder::default()
|
||||||
|
.existential_deposit(256)
|
||||||
|
.build()
|
||||||
|
.execute_with(|| {
|
||||||
|
assert_eq!(System::block_number(), 1);
|
||||||
|
let user2_free_balance = Balances::free_balance(&2);
|
||||||
|
let user4_free_balance = Balances::free_balance(&4);
|
||||||
|
assert_eq!(user2_free_balance, 256 * 20);
|
||||||
|
assert_eq!(user4_free_balance, 256 * 40);
|
||||||
|
// Account 2 should already have a vesting schedule.
|
||||||
|
let user2_vesting_schedule = VestingInfo {
|
||||||
|
locked: 256 * 20,
|
||||||
|
per_block: 256, // Vesting over 20 blocks
|
||||||
|
starting_block: 10,
|
||||||
|
};
|
||||||
|
assert_eq!(Vesting::vesting(&2), Some(user2_vesting_schedule));
|
||||||
|
|
||||||
|
// The vesting schedule we will try to create, fails due to pre-existence of schedule.
|
||||||
|
let new_vesting_schedule = VestingInfo {
|
||||||
|
locked: 256 * 5,
|
||||||
|
per_block: 64, // Vesting over 20 blocks
|
||||||
|
starting_block: 10,
|
||||||
|
};
|
||||||
|
assert_noop!(
|
||||||
|
Vesting::vested_transfer(Some(4).into(), 2, new_vesting_schedule),
|
||||||
|
Error::<Test>::ExistingVestingSchedule,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Fails due to too low transfer amount.
|
||||||
|
let new_vesting_schedule_too_low = VestingInfo {
|
||||||
|
locked: 256 * 1,
|
||||||
|
per_block: 64,
|
||||||
|
starting_block: 10,
|
||||||
|
};
|
||||||
|
assert_noop!(
|
||||||
|
Vesting::vested_transfer(Some(3).into(), 4, new_vesting_schedule_too_low),
|
||||||
|
Error::<Test>::AmountLow,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Verify no currency transfer happened.
|
||||||
|
assert_eq!(user2_free_balance, 256 * 20);
|
||||||
|
assert_eq!(user4_free_balance, 256 * 40);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user