use codec::EncodeLike;
use pezframe_support::{assert_noop, assert_ok, assert_storage_noop};
use pezframe_system::RawOrigin;
use pezsp_runtime::{
traits::{BadOrigin, Identity},
TokenError,
};
use super::{Vesting as VestingStorage, *};
use crate::mock::{vesting_events_since_last_call, Balances, ExtBuilder, System, Test, Vesting};
const ED: u64 = 256;
fn vest_and_assert_no_vesting<T>(account: u64)
where
u64: EncodeLike<<T as pezframe_system::Config>::AccountId>,
T: pezpallet::Config,
{
let _result = Vesting::vest(Some(account).into());
assert!(!<VestingStorage<T>>::contains_key(account));
}
#[test]
fn check_vesting_status() {
ExtBuilder::default().existential_deposit(ED).build().execute_with(|| {
let user1_free_balance = Balances::free_balance(&1);
let user2_free_balance = Balances::free_balance(&2);
let user12_free_balance = Balances::free_balance(&12);
assert_eq!(user1_free_balance, ED * 10); assert_eq!(user2_free_balance, ED * 20); assert_eq!(user12_free_balance, ED * 10); let user1_vesting_schedule = VestingInfo::new(
ED * 5,
128, 0,
);
let user2_vesting_schedule = VestingInfo::new(
ED * 20,
ED, 10,
);
let user12_vesting_schedule = VestingInfo::new(
ED * 5,
64, 10,
);
assert_eq!(VestingStorage::<Test>::get(&1).unwrap(), vec![user1_vesting_schedule]); assert_eq!(VestingStorage::<Test>::get(&2).unwrap(), vec![user2_vesting_schedule]); assert_eq!(VestingStorage::<Test>::get(&12).unwrap(), vec![user12_vesting_schedule]);
assert_eq!(Vesting::vesting_balance(&1), Some(128 * 9));
assert_eq!(Vesting::vesting_balance(&2), Some(user2_free_balance));
assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - ED * 5));
System::set_block_number(10);
assert_eq!(System::block_number(), 10);
assert_eq!(Vesting::vesting_balance(&1), Some(0));
assert_eq!(Vesting::vesting_balance(&2), Some(user2_free_balance));
assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - ED * 5));
System::set_block_number(30);
assert_eq!(System::block_number(), 30);
assert_eq!(Vesting::vesting_balance(&1), Some(0)); assert_eq!(Vesting::vesting_balance(&2), Some(0)); assert_eq!(Vesting::vesting_balance(&12), Some(0));
vest_and_assert_no_vesting::<Test>(1);
vest_and_assert_no_vesting::<Test>(2);
vest_and_assert_no_vesting::<Test>(12);
});
}
#[test]
fn check_vesting_status_for_multi_schedule_account() {
ExtBuilder::default().existential_deposit(ED).build().execute_with(|| {
assert_eq!(System::block_number(), 1);
let sched0 = VestingInfo::new(
ED * 20,
ED, 10,
);
assert_eq!(VestingStorage::<Test>::get(&2).unwrap(), vec![sched0]);
let free_balance = Balances::free_balance(&2);
assert_eq!(free_balance, ED * (20));
assert_eq!(Vesting::vesting_balance(&2), Some(free_balance));
let sched1 = VestingInfo::new(
ED * 10,
ED, 0,
);
assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched1));
let free_balance = Balances::free_balance(&2);
assert_eq!(free_balance, ED * (10 + 20));
assert_eq!(VestingStorage::<Test>::get(&2).unwrap(), vec![sched0, sched1]);
assert_eq!(Vesting::vesting_balance(&2), Some(free_balance - sched1.per_block()));
let sched2 = VestingInfo::new(
ED * 30,
ED, 5,
);
assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched2));
System::set_block_number(9);
let free_balance = Balances::free_balance(&2);
assert_eq!(free_balance, ED * (10 + 20 + 30));
assert_eq!(
Vesting::vesting_balance(&2),
Some(free_balance - sched1.per_block() * 9 - sched2.per_block() * 4)
);
System::set_block_number(20);
assert_eq!(
Vesting::vesting_balance(&2),
Some(
free_balance - sched1.locked() - sched2.per_block() * 15 - sched0.per_block() * 10
)
);
System::set_block_number(30);
assert_eq!(
Vesting::vesting_balance(&2),
Some(free_balance - sched1.locked() - sched2.per_block() * 25 - sched0.locked())
);
System::set_block_number(35);
assert_eq!(Vesting::vesting_balance(&2), Some(0));
assert_eq!(VestingStorage::<Test>::get(&2).unwrap(), vec![sched0, sched1, sched2]);
vest_and_assert_no_vesting::<Test>(2);
});
}
#[test]
fn unvested_balance_should_not_transfer() {
ExtBuilder::default().existential_deposit(10).build().execute_with(|| {
let user1_free_balance = Balances::free_balance(&1);
assert_eq!(user1_free_balance, 100); assert_eq!(Vesting::vesting_balance(&1), Some(45));
assert_noop!(Balances::transfer_allow_death(Some(1).into(), 2, 56), TokenError::Frozen);
});
}
#[test]
fn vested_balance_should_transfer() {
ExtBuilder::default().existential_deposit(10).build().execute_with(|| {
let user1_free_balance = Balances::free_balance(&1);
assert_eq!(user1_free_balance, 100); assert_eq!(Vesting::vesting_balance(&1), Some(45));
assert_ok!(Vesting::vest(Some(1).into()));
assert_ok!(Balances::transfer_allow_death(Some(1).into(), 2, 55));
});
}
#[test]
fn vested_balance_should_transfer_with_multi_sched() {
ExtBuilder::default().existential_deposit(ED).build().execute_with(|| {
let sched0 = VestingInfo::new(5 * ED, 128, 0);
assert_ok!(Vesting::vested_transfer(Some(13).into(), 1, sched0));
assert_eq!(VestingStorage::<Test>::get(&1).unwrap(), vec![sched0, sched0]);
let user1_free_balance = Balances::free_balance(&1);
assert_eq!(user1_free_balance, 3840);
assert_eq!(Vesting::vesting_balance(&1), Some(2304));
assert_ok!(Vesting::vest(Some(1).into()));
assert_ok!(Balances::transfer_allow_death(Some(1).into(), 2, 1536));
});
}
#[test]
fn non_vested_cannot_vest() {
ExtBuilder::default().existential_deposit(ED).build().execute_with(|| {
assert!(!<VestingStorage<Test>>::contains_key(4));
assert_noop!(Vesting::vest(Some(4).into()), Error::<Test>::NotVesting);
});
}
#[test]
fn vested_balance_should_transfer_using_vest_other() {
ExtBuilder::default().existential_deposit(10).build().execute_with(|| {
let user1_free_balance = Balances::free_balance(&1);
assert_eq!(user1_free_balance, 100); assert_eq!(Vesting::vesting_balance(&1), Some(45));
assert_ok!(Vesting::vest_other(Some(2).into(), 1));
assert_ok!(Balances::transfer_allow_death(Some(1).into(), 2, 55));
});
}
#[test]
fn vested_balance_should_transfer_using_vest_other_with_multi_sched() {
ExtBuilder::default().existential_deposit(ED).build().execute_with(|| {
let sched0 = VestingInfo::new(5 * ED, 128, 0);
assert_ok!(Vesting::vested_transfer(Some(13).into(), 1, sched0));
assert_eq!(VestingStorage::<Test>::get(&1).unwrap(), vec![sched0, sched0]);
let user1_free_balance = Balances::free_balance(&1);
assert_eq!(user1_free_balance, 3840);
assert_eq!(Vesting::vesting_balance(&1), Some(2304));
assert_ok!(Vesting::vest_other(Some(2).into(), 1));
assert_ok!(Balances::transfer_allow_death(Some(1).into(), 2, 1536));
});
}
#[test]
fn non_vested_cannot_vest_other() {
ExtBuilder::default().existential_deposit(ED).build().execute_with(|| {
assert!(!<VestingStorage<Test>>::contains_key(4));
assert_noop!(Vesting::vest_other(Some(3).into(), 4), Error::<Test>::NotVesting);
});
}
#[test]
fn extra_balance_should_transfer() {
ExtBuilder::default().existential_deposit(10).build().execute_with(|| {
assert_ok!(Balances::transfer_allow_death(Some(3).into(), 1, 100));
assert_ok!(Balances::transfer_allow_death(Some(3).into(), 2, 100));
let user1_free_balance = Balances::free_balance(&1);
assert_eq!(user1_free_balance, 200);
let user2_free_balance = Balances::free_balance(&2);
assert_eq!(user2_free_balance, 300);
assert_eq!(Vesting::vesting_balance(&1), Some(45));
assert_ok!(Vesting::vest(Some(1).into()));
assert_ok!(Balances::transfer_allow_death(Some(1).into(), 3, 155));
assert_eq!(Vesting::vesting_balance(&2), Some(200));
assert_ok!(Vesting::vest(Some(2).into()));
assert_ok!(Balances::transfer_allow_death(Some(2).into(), 3, 100));
});
}
#[test]
fn liquid_funds_should_transfer_with_delayed_vesting() {
ExtBuilder::default().existential_deposit(256).build().execute_with(|| {
let user12_free_balance = Balances::free_balance(&12);
assert_eq!(user12_free_balance, 2560);
assert_eq!(Vesting::vesting_balance(&12), Some(user12_free_balance - 256 * 5));
let user12_vesting_schedule = VestingInfo::new(
256 * 5,
64,
10,
);
assert_eq!(VestingStorage::<Test>::get(&12).unwrap(), vec![user12_vesting_schedule]);
assert_ok!(Balances::transfer_allow_death(Some(12).into(), 3, 256 * 5));
});
}
#[test]
fn vested_transfer_works() {
ExtBuilder::default().existential_deposit(256).build().execute_with(|| {
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);
assert_eq!(VestingStorage::<Test>::get(&4), None);
let new_vesting_schedule = VestingInfo::new(
256 * 5,
64, 10,
);
assert_ok!(Vesting::vested_transfer(Some(3).into(), 4, new_vesting_schedule));
assert_eq!(
vesting_events_since_last_call(),
vec![
Event::VestingCreated { account: 4, schedule_index: 0 },
Event::VestingUpdated { account: 4, unvested: 1280 },
]
);
assert_eq!(VestingStorage::<Test>::get(&4).unwrap(), vec![new_vesting_schedule]);
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);
assert_eq!(Vesting::vesting_balance(&4), Some(256 * 5));
System::set_block_number(20);
assert_eq!(System::block_number(), 20);
assert_eq!(Vesting::vesting_balance(&4), Some(10 * 64));
System::set_block_number(30);
assert_eq!(System::block_number(), 30);
assert_eq!(Vesting::vesting_balance(&4), Some(0));
vest_and_assert_no_vesting::<Test>(4);
});
}
#[test]
fn vested_transfer_correctly_fails() {
ExtBuilder::default().existential_deposit(ED).build().execute_with(|| {
let user2_free_balance = Balances::free_balance(&2);
let user4_free_balance = Balances::free_balance(&4);
assert_eq!(user2_free_balance, ED * 20);
assert_eq!(user4_free_balance, ED * 40);
let user2_vesting_schedule = VestingInfo::new(
ED * 20,
ED, 10,
);
assert_eq!(VestingStorage::<Test>::get(&2).unwrap(), vec![user2_vesting_schedule]);
let new_vesting_schedule_too_low =
VestingInfo::new(<Test as Config>::MinVestedTransfer::get() - 1, 64, 10);
assert_noop!(
Vesting::vested_transfer(Some(3).into(), 4, new_vesting_schedule_too_low),
Error::<Test>::AmountLow,
);
let schedule_per_block_0 =
VestingInfo::new(<Test as Config>::MinVestedTransfer::get(), 0, 10);
assert_noop!(
Vesting::vested_transfer(Some(13).into(), 4, schedule_per_block_0),
Error::<Test>::InvalidScheduleParams,
);
let schedule_locked_0 = VestingInfo::new(0, 1, 10);
assert_noop!(
Vesting::vested_transfer(Some(3).into(), 4, schedule_locked_0),
Error::<Test>::AmountLow,
);
assert_eq!(user2_free_balance, Balances::free_balance(&2));
assert_eq!(user4_free_balance, Balances::free_balance(&4));
vest_and_assert_no_vesting::<Test>(4);
});
}
#[test]
fn vested_transfer_allows_max_schedules() {
ExtBuilder::default().existential_deposit(ED).build().execute_with(|| {
let mut user_4_free_balance = Balances::free_balance(&4);
let max_schedules = <Test as Config>::MAX_VESTING_SCHEDULES;
let sched = VestingInfo::new(
<Test as Config>::MinVestedTransfer::get(),
1, 10,
);
for _ in 0..max_schedules {
assert_ok!(Vesting::vested_transfer(Some(13).into(), 4, sched));
}
let transferred_amount = <Test as Config>::MinVestedTransfer::get() * max_schedules as u64;
assert_eq!(Vesting::vesting_balance(&4), Some(transferred_amount));
user_4_free_balance += transferred_amount;
assert_eq!(Balances::free_balance(&4), user_4_free_balance);
assert_noop!(
Vesting::vested_transfer(Some(3).into(), 4, sched),
Error::<Test>::AtMaxVestingSchedules,
);
assert_eq!(Balances::free_balance(&4), user_4_free_balance);
System::set_block_number(
<Test as Config>::MinVestedTransfer::get() + sched.starting_block(),
);
assert_eq!(Vesting::vesting_balance(&4), Some(0));
vest_and_assert_no_vesting::<Test>(4);
});
}
#[test]
fn force_vested_transfer_works() {
ExtBuilder::default().existential_deposit(ED).build().execute_with(|| {
let user3_free_balance = Balances::free_balance(&3);
let user4_free_balance = Balances::free_balance(&4);
assert_eq!(user3_free_balance, ED * 30);
assert_eq!(user4_free_balance, ED * 40);
assert_eq!(VestingStorage::<Test>::get(&4), None);
let new_vesting_schedule = VestingInfo::new(
ED * 5,
64, 10,
);
assert_noop!(
Vesting::force_vested_transfer(Some(4).into(), 3, 4, new_vesting_schedule),
BadOrigin
);
assert_ok!(Vesting::force_vested_transfer(
RawOrigin::Root.into(),
3,
4,
new_vesting_schedule
));
assert_eq!(
vesting_events_since_last_call(),
vec![
Event::VestingCreated { account: 4, schedule_index: 0 },
Event::VestingUpdated { account: 4, unvested: 1280 },
]
);
assert_eq!(VestingStorage::<Test>::get(&4).unwrap()[0], new_vesting_schedule);
assert_eq!(VestingStorage::<Test>::get(&4).unwrap().len(), 1);
let user3_free_balance_updated = Balances::free_balance(&3);
assert_eq!(user3_free_balance_updated, ED * 25);
let user4_free_balance_updated = Balances::free_balance(&4);
assert_eq!(user4_free_balance_updated, ED * 45);
assert_eq!(Vesting::vesting_balance(&4), Some(ED * 5));
System::set_block_number(20);
assert_eq!(System::block_number(), 20);
assert_eq!(Vesting::vesting_balance(&4), Some(10 * 64));
System::set_block_number(30);
assert_eq!(System::block_number(), 30);
assert_eq!(Vesting::vesting_balance(&4), Some(0));
vest_and_assert_no_vesting::<Test>(4);
});
}
#[test]
fn force_vested_transfer_correctly_fails() {
ExtBuilder::default().existential_deposit(ED).build().execute_with(|| {
let user2_free_balance = Balances::free_balance(&2);
let user4_free_balance = Balances::free_balance(&4);
assert_eq!(user2_free_balance, ED * 20);
assert_eq!(user4_free_balance, ED * 40);
let user2_vesting_schedule = VestingInfo::new(
ED * 20,
ED, 10,
);
assert_eq!(VestingStorage::<Test>::get(&2).unwrap(), vec![user2_vesting_schedule]);
let new_vesting_schedule_too_low =
VestingInfo::new(<Test as Config>::MinVestedTransfer::get() - 1, 64, 10);
assert_noop!(
Vesting::force_vested_transfer(
RawOrigin::Root.into(),
3,
4,
new_vesting_schedule_too_low
),
Error::<Test>::AmountLow,
);
let schedule_per_block_0 =
VestingInfo::new(<Test as Config>::MinVestedTransfer::get(), 0, 10);
assert_noop!(
Vesting::force_vested_transfer(RawOrigin::Root.into(), 13, 4, schedule_per_block_0),
Error::<Test>::InvalidScheduleParams,
);
let schedule_locked_0 = VestingInfo::new(0, 1, 10);
assert_noop!(
Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, schedule_locked_0),
Error::<Test>::AmountLow,
);
assert_eq!(user2_free_balance, Balances::free_balance(&2));
assert_eq!(user4_free_balance, Balances::free_balance(&4));
vest_and_assert_no_vesting::<Test>(4);
});
}
#[test]
fn force_vested_transfer_allows_max_schedules() {
ExtBuilder::default().existential_deposit(ED).build().execute_with(|| {
let mut user_4_free_balance = Balances::free_balance(&4);
let max_schedules = <Test as Config>::MAX_VESTING_SCHEDULES;
let sched = VestingInfo::new(
<Test as Config>::MinVestedTransfer::get(),
1, 10,
);
for _ in 0..max_schedules {
assert_ok!(Vesting::force_vested_transfer(RawOrigin::Root.into(), 13, 4, sched));
}
let transferred_amount = <Test as Config>::MinVestedTransfer::get() * max_schedules as u64;
assert_eq!(Vesting::vesting_balance(&4), Some(transferred_amount));
user_4_free_balance += transferred_amount;
assert_eq!(Balances::free_balance(&4), user_4_free_balance);
assert_noop!(
Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 4, sched),
Error::<Test>::AtMaxVestingSchedules,
);
assert_eq!(Balances::free_balance(&4), user_4_free_balance);
System::set_block_number(<Test as Config>::MinVestedTransfer::get() + 10);
assert_eq!(Vesting::vesting_balance(&4), Some(0));
vest_and_assert_no_vesting::<Test>(4);
});
}
#[test]
fn merge_schedules_that_have_not_started() {
ExtBuilder::default().existential_deposit(ED).build().execute_with(|| {
let sched0 = VestingInfo::new(
ED * 20,
ED, 10,
);
assert_eq!(VestingStorage::<Test>::get(&2).unwrap(), vec![sched0]);
assert_eq!(Balances::usable_balance(&2), 0);
assert_ok!(Vesting::vested_transfer(Some(3).into(), 2, sched0));
assert_eq!(VestingStorage::<Test>::get(&2).unwrap(), vec![sched0, sched0]);
assert_eq!(Balances::usable_balance(&2), 0);
assert_ok!(Vesting::merge_schedules(Some(2).into(), 0, 1));
let sched1 = VestingInfo::new(
sched0.locked() * 2,
sched0.per_block() * 2,
10, );
assert_eq!(VestingStorage::<Test>::get(&2).unwrap(), vec![sched1]);
assert_eq!(Balances::usable_balance(&2), 0);
});
}
#[test]
fn merge_ongoing_schedules() {
ExtBuilder::default().existential_deposit(ED).build().execute_with(|| {
let sched0 = VestingInfo::new(
ED * 20,
ED, 10,
);
assert_eq!(VestingStorage::<Test>::get(&2).unwrap(), vec![sched0]);
let sched1 = VestingInfo::new(
ED * 10,
ED,
sched0.starting_block() + 5,
);
assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched1));
assert_eq!(VestingStorage::<Test>::get(&2).unwrap(), vec![sched0, sched1]);
let cur_block = 20;
System::set_block_number(cur_block);
assert_eq!(Balances::usable_balance(&2), 0);
assert_ok!(Vesting::merge_schedules(Some(2).into(), 0, 1));
let sched0_vested_now = sched0.per_block() * (cur_block - sched0.starting_block());
let sched1_vested_now = sched1.per_block() * (cur_block - sched1.starting_block());
assert_eq!(Balances::usable_balance(&2), sched0_vested_now + sched1_vested_now);
let sched2_locked = sched1
.locked_at::<Identity>(cur_block)
.saturating_add(sched0.locked_at::<Identity>(cur_block));
let sched2_end = sched1
.ending_block_as_balance::<Identity>()
.max(sched0.ending_block_as_balance::<Identity>());
let sched2_duration = sched2_end - cur_block;
let sched2_per_block = sched2_locked / sched2_duration;
let sched2 = VestingInfo::new(sched2_locked, sched2_per_block, cur_block);
assert_eq!(VestingStorage::<Test>::get(&2).unwrap(), vec![sched2]);
System::set_block_number(30);
vest_and_assert_no_vesting::<Test>(2);
});
}
#[test]
fn merging_shifts_other_schedules_index() {
ExtBuilder::default().existential_deposit(ED).build().execute_with(|| {
let sched0 = VestingInfo::new(
ED * 10,
ED, 10,
);
let sched1 = VestingInfo::new(
ED * 11,
ED, 11,
);
let sched2 = VestingInfo::new(
ED * 12,
ED, 12,
);
assert_eq!(VestingStorage::<Test>::get(&3), None);
let usable_balance = Balances::usable_balance(&3);
assert_eq!(usable_balance, 30 * ED);
let cur_block = 1;
assert_eq!(System::block_number(), cur_block);
assert_ok!(Vesting::vested_transfer(Some(4).into(), 3, sched0));
assert_ok!(Vesting::vested_transfer(Some(4).into(), 3, sched1));
assert_ok!(Vesting::vested_transfer(Some(4).into(), 3, sched2));
assert_eq!(VestingStorage::<Test>::get(&3).unwrap(), vec![sched0, sched1, sched2]);
assert_eq!(usable_balance, Balances::usable_balance(&3));
assert_ok!(Vesting::merge_schedules(Some(3).into(), 0, 2));
let sched3_start = sched1.starting_block().max(sched2.starting_block());
let sched3_locked =
sched2.locked_at::<Identity>(cur_block) + sched0.locked_at::<Identity>(cur_block);
let sched3_end = sched2
.ending_block_as_balance::<Identity>()
.max(sched0.ending_block_as_balance::<Identity>());
let sched3_duration = sched3_end - sched3_start;
let sched3_per_block = sched3_locked / sched3_duration;
let sched3 = VestingInfo::new(sched3_locked, sched3_per_block, sched3_start);
assert_eq!(VestingStorage::<Test>::get(&3).unwrap(), vec![sched1, sched3]);
assert_eq!(Balances::usable_balance(&3), usable_balance);
});
}
#[test]
fn merge_ongoing_and_yet_to_be_started_schedules() {
ExtBuilder::default().existential_deposit(ED).build().execute_with(|| {
let sched0 = VestingInfo::new(
ED * 20,
ED, 10,
);
assert_eq!(VestingStorage::<Test>::get(&2).unwrap(), vec![sched0]);
let mut cur_block =
(sched0.starting_block() + sched0.ending_block_as_balance::<Identity>()) / 2;
assert_eq!(cur_block, 20);
System::set_block_number(cur_block);
let mut usable_balance = 0;
assert_eq!(Balances::usable_balance(&2), usable_balance);
Vesting::vest(Some(2).into()).unwrap();
let sched0_vested_now = sched0.locked() - sched0.locked_at::<Identity>(cur_block);
usable_balance += sched0_vested_now;
assert_eq!(Balances::usable_balance(&2), usable_balance);
cur_block += 1;
System::set_block_number(cur_block);
let sched1 = VestingInfo::new(
ED * 10,
1, cur_block + 1,
);
assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched1));
assert_ok!(Vesting::merge_schedules(Some(2).into(), 0, 1));
usable_balance += sched0.per_block();
assert_eq!(Balances::usable_balance(&2), usable_balance);
let sched2_start = sched1.starting_block();
let sched2_locked =
sched0.locked_at::<Identity>(cur_block) + sched1.locked_at::<Identity>(cur_block);
let sched2_end = sched0
.ending_block_as_balance::<Identity>()
.max(sched1.ending_block_as_balance::<Identity>());
let sched2_duration = sched2_end - sched2_start;
let sched2_per_block = sched2_locked / sched2_duration;
let sched2 = VestingInfo::new(sched2_locked, sched2_per_block, sched2_start);
assert_eq!(VestingStorage::<Test>::get(&2).unwrap(), vec![sched2]);
});
}
#[test]
fn merge_finished_and_ongoing_schedules() {
ExtBuilder::default().existential_deposit(ED).build().execute_with(|| {
let sched0 = VestingInfo::new(
ED * 20,
ED, 10,
);
assert_eq!(VestingStorage::<Test>::get(&2).unwrap(), vec![sched0]);
let sched1 = VestingInfo::new(
ED * 40,
ED, 10,
);
assert_ok!(Vesting::vested_transfer(Some(4).into(), 2, sched1));
let sched2 = VestingInfo::new(
ED * 30,
ED, 10,
);
assert_ok!(Vesting::vested_transfer(Some(3).into(), 2, sched2));
assert_eq!(VestingStorage::<Test>::get(&2).unwrap(), vec![sched0, sched1, sched2]);
let cur_block = sched0.ending_block_as_balance::<Identity>();
System::set_block_number(cur_block);
assert_eq!(System::block_number(), 30);
assert_eq!(Balances::usable_balance(&2), 0);
assert_ok!(Vesting::merge_schedules(Some(2).into(), 0, 1));
assert_eq!(VestingStorage::<Test>::get(&2).unwrap(), vec![sched2, sched1]);
let sched0_unlocked_now = sched0.locked();
let sched1_unlocked_now = sched1.locked() - sched1.locked_at::<Identity>(cur_block);
let sched2_unlocked_now = sched2.locked() - sched2.locked_at::<Identity>(cur_block);
assert_eq!(
Balances::usable_balance(&2),
sched0_unlocked_now + sched1_unlocked_now + sched2_unlocked_now
);
});
}
#[test]
fn merge_finishing_schedules_does_not_create_a_new_one() {
ExtBuilder::default().existential_deposit(ED).build().execute_with(|| {
let sched0 = VestingInfo::new(
ED * 20,
ED, 10,
);
assert_eq!(VestingStorage::<Test>::get(&2).unwrap(), vec![sched0]);
let sched1 = VestingInfo::new(
ED * 30,
ED, 10,
);
assert_ok!(Vesting::vested_transfer(Some(3).into(), 2, sched1));
assert_eq!(VestingStorage::<Test>::get(&2).unwrap(), vec![sched0, sched1]);
let all_scheds_end = sched0
.ending_block_as_balance::<Identity>()
.max(sched1.ending_block_as_balance::<Identity>());
assert_eq!(all_scheds_end, 40);
System::set_block_number(all_scheds_end);
assert_eq!(Balances::usable_balance(&2), 0);
assert_ok!(Vesting::merge_schedules(Some(2).into(), 0, 1));
assert!(!<VestingStorage<Test>>::contains_key(&2));
assert_eq!(Balances::usable_balance(&2), sched0.locked() + sched1.locked());
});
}
#[test]
fn merge_finished_and_yet_to_be_started_schedules() {
ExtBuilder::default().existential_deposit(ED).build().execute_with(|| {
let sched0 = VestingInfo::new(
ED * 20,
ED, 10, );
assert_eq!(VestingStorage::<Test>::get(&2).unwrap(), vec![sched0]);
let sched1 = VestingInfo::new(
ED * 30,
ED * 2, 35,
);
assert_ok!(Vesting::vested_transfer(Some(13).into(), 2, sched1));
assert_eq!(VestingStorage::<Test>::get(&2).unwrap(), vec![sched0, sched1]);
let sched2 = VestingInfo::new(
ED * 40,
ED, 30,
);
assert_ok!(Vesting::vested_transfer(Some(13).into(), 2, sched2));
assert_eq!(VestingStorage::<Test>::get(&2).unwrap(), vec![sched0, sched1, sched2]);
System::set_block_number(30);
assert_eq!(Vesting::vesting_balance(&2), Some(sched1.locked() + sched2.locked()));
assert_eq!(Balances::usable_balance(&2), 0);
assert_ok!(Vesting::merge_schedules(Some(2).into(), 0, 1));
assert_eq!(VestingStorage::<Test>::get(&2).unwrap(), vec![sched2, sched1]);
assert_eq!(Balances::usable_balance(&2), sched0.locked());
});
}
#[test]
fn merge_schedules_throws_proper_errors() {
ExtBuilder::default().existential_deposit(ED).build().execute_with(|| {
let sched0 = VestingInfo::new(
ED * 20,
ED, 10,
);
assert_eq!(VestingStorage::<Test>::get(&2).unwrap(), vec![sched0]);
assert_noop!(
Vesting::merge_schedules(Some(2).into(), 0, 1),
Error::<Test>::ScheduleIndexOutOfBounds
);
assert_eq!(VestingStorage::<Test>::get(&4), None);
assert_noop!(Vesting::merge_schedules(Some(4).into(), 0, 1), Error::<Test>::NotVesting);
Vesting::vested_transfer(Some(3).into(), 2, sched0).unwrap();
assert_eq!(VestingStorage::<Test>::get(&2).unwrap(), vec![sched0, sched0]);
assert_noop!(
Vesting::merge_schedules(Some(2).into(), 0, 2),
Error::<Test>::ScheduleIndexOutOfBounds
);
assert_storage_noop!(Vesting::merge_schedules(Some(2).into(), 0, 0).unwrap());
});
}
#[test]
fn generates_multiple_schedules_from_genesis_config() {
let vesting_config = vec![
(1, 0, 10, 5 * ED),
(2, 10, 20, 19 * ED),
(2, 10, 20, 18 * ED),
(12, 10, 20, 9 * ED),
(12, 10, 20, 8 * ED),
(12, 10, 20, 7 * ED),
];
ExtBuilder::default()
.existential_deposit(ED)
.vesting_genesis_config(vesting_config)
.build()
.execute_with(|| {
let user1_sched1 = VestingInfo::new(5 * ED, 128, 0u64);
assert_eq!(VestingStorage::<Test>::get(&1).unwrap(), vec![user1_sched1]);
let user2_sched1 = VestingInfo::new(1 * ED, 12, 10u64);
let user2_sched2 = VestingInfo::new(2 * ED, 25, 10u64);
assert_eq!(VestingStorage::<Test>::get(&2).unwrap(), vec![user2_sched1, user2_sched2]);
let user12_sched1 = VestingInfo::new(1 * ED, 12, 10u64);
let user12_sched2 = VestingInfo::new(2 * ED, 25, 10u64);
let user12_sched3 = VestingInfo::new(3 * ED, 38, 10u64);
assert_eq!(
VestingStorage::<Test>::get(&12).unwrap(),
vec![user12_sched1, user12_sched2, user12_sched3]
);
});
}
#[test]
#[should_panic]
fn multiple_schedules_from_genesis_config_errors() {
let vesting_config =
vec![(12, 10, 20, ED), (12, 10, 20, ED), (12, 10, 20, ED), (12, 10, 20, ED)];
ExtBuilder::default()
.existential_deposit(ED)
.vesting_genesis_config(vesting_config)
.build();
}
#[test]
fn build_genesis_has_storage_version_v1() {
ExtBuilder::default().existential_deposit(ED).build().execute_with(|| {
assert_eq!(StorageVersion::<Test>::get(), Releases::V1);
});
}
#[test]
fn merge_vesting_handles_per_block_0() {
ExtBuilder::default().existential_deposit(ED).build().execute_with(|| {
let sched0 = VestingInfo::new(
ED, 0, 1,
);
assert_eq!(sched0.ending_block_as_balance::<Identity>(), 257);
let sched1 = VestingInfo::new(
ED * 2,
0, 10,
);
assert_eq!(sched1.ending_block_as_balance::<Identity>(), 512u64 + 10);
let merged = VestingInfo::new(764, 1, 10);
assert_eq!(Vesting::merge_vesting_info(5, sched0, sched1), Some(merged));
});
}
#[test]
fn vesting_info_validate_works() {
let min_transfer = <Test as Config>::MinVestedTransfer::get();
assert_eq!(VestingInfo::new(min_transfer - 1, 1u64, 10u64).is_valid(), true);
assert_eq!(VestingInfo::new(0, 1u64, 10u64).is_valid(), false);
assert_eq!(VestingInfo::new(min_transfer + 1, 0u64, 10u64).is_valid(), false);
assert_eq!(VestingInfo::new(min_transfer, 1u64, 10u64).is_valid(), true);
}
#[test]
fn vesting_info_ending_block_as_balance_works() {
let per_block_0 = VestingInfo::new(256u32, 0u32, 10u32);
assert_eq!(per_block_0.ending_block_as_balance::<Identity>(), 256 + 10);
let per_block_gt_locked = VestingInfo::new(256u32, 256 * 2u32, 10u32);
assert_eq!(
per_block_gt_locked.ending_block_as_balance::<Identity>(),
1 + per_block_gt_locked.starting_block()
);
let per_block_eq_locked = VestingInfo::new(256u32, 256u32, 10u32);
assert_eq!(
per_block_gt_locked.ending_block_as_balance::<Identity>(),
per_block_eq_locked.ending_block_as_balance::<Identity>()
);
let imperfect_per_block = VestingInfo::new(256u32, 250u32, 10u32);
assert_eq!(
imperfect_per_block.ending_block_as_balance::<Identity>(),
imperfect_per_block.starting_block() + 2u32,
);
assert_eq!(
imperfect_per_block
.locked_at::<Identity>(imperfect_per_block.ending_block_as_balance::<Identity>()),
0
);
}
#[test]
fn per_block_works() {
let per_block_0 = VestingInfo::new(256u32, 0u32, 10u32);
assert_eq!(per_block_0.per_block(), 1u32);
assert_eq!(per_block_0.raw_per_block(), 0u32);
let per_block_1 = VestingInfo::new(256u32, 1u32, 10u32);
assert_eq!(per_block_1.per_block(), 1u32);
assert_eq!(per_block_1.raw_per_block(), 1u32);
}
#[test]
fn vested_transfer_less_than_existential_deposit_fails() {
ExtBuilder::default().existential_deposit(4 * ED).build().execute_with(|| {
assert!(
<Test as Config>::Currency::minimum_balance()
> <Test as Config>::MinVestedTransfer::get()
);
let sched =
VestingInfo::new(<Test as Config>::MinVestedTransfer::get() as u64, 1u64, 10u64);
assert!(
Balances::free_balance(&99) + sched.locked()
< <Test as Config>::Currency::minimum_balance()
);
assert_noop!(Vesting::vested_transfer(Some(3).into(), 99, sched), TokenError::BelowMinimum,);
assert_noop!(
Vesting::force_vested_transfer(RawOrigin::Root.into(), 3, 99, sched),
TokenError::BelowMinimum,
);
});
}
#[test]
fn remove_vesting_schedule() {
ExtBuilder::default().existential_deposit(ED).build().execute_with(|| {
assert_eq!(Balances::free_balance(&3), 256 * 30);
assert_eq!(Balances::free_balance(&4), 256 * 40);
assert_eq!(VestingStorage::<Test>::get(&4), None);
let new_vesting_schedule = VestingInfo::new(
ED * 5,
(ED * 5) / 20, 10,
);
assert_ok!(Vesting::vested_transfer(Some(3).into(), 4, new_vesting_schedule));
assert_eq!(
vesting_events_since_last_call(),
vec![
Event::VestingCreated { account: 4, schedule_index: 0 },
Event::VestingUpdated { account: 4, unvested: 1280 },
]
);
assert_eq!(VestingStorage::<Test>::get(&4).unwrap(), vec![new_vesting_schedule]);
assert_eq!(Vesting::vesting_balance(&4), Some(256 * 5));
assert_noop!(Vesting::force_remove_vesting_schedule(Some(4).into(), 4, 0), BadOrigin);
assert_ok!(Vesting::force_remove_vesting_schedule(RawOrigin::Root.into(), 4, 0));
System::assert_last_event(Event::VestingCompleted { account: 4 }.into());
assert!(!<VestingStorage<Test>>::contains_key(4));
assert_eq!(VestingStorage::<Test>::get(&4), None);
assert_noop!(
Vesting::force_remove_vesting_schedule(RawOrigin::Root.into(), 4, 0),
Error::<Test>::InvalidScheduleParams
);
});
}
#[test]
fn vested_transfer_impl_works() {
ExtBuilder::default().existential_deposit(ED).build().execute_with(|| {
assert_eq!(Balances::free_balance(&3), 256 * 30);
assert_eq!(Balances::free_balance(&4), 256 * 40);
assert_eq!(VestingStorage::<Test>::get(&4), None);
assert_ok!(<Vesting as VestedTransfer<_>>::vested_transfer(
&3,
&4,
ED * 5,
ED * 5 / 20,
10
));
let new_vesting_schedule = VestingInfo::new(
ED * 5,
(ED * 5) / 20, 10,
);
assert_eq!(VestingStorage::<Test>::get(&4).unwrap(), vec![new_vesting_schedule]);
assert_eq!(Vesting::vesting_balance(&4), Some(256 * 5));
assert_noop!(
<Vesting as VestedTransfer<_>>::vested_transfer(&3, &4, ED * 9999, ED * 5 / 20, 10),
TokenError::FundsUnavailable
);
assert_noop!(
<Vesting as VestedTransfer<_>>::vested_transfer(&3, &4, ED * 5, 0, 10),
Error::<Test>::InvalidScheduleParams
);
});
}