diff --git a/vesting/Cargo.toml b/vesting/Cargo.toml index c56165a9b..a9362a0e0 100644 --- a/vesting/Cargo.toml +++ b/vesting/Cargo.toml @@ -12,6 +12,7 @@ parity-scale-codec = { workspace = true, default-features = false, features = [" scale-info = { workspace = true } serde = { workspace = true, optional = true } +frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } sp-io = { workspace = true } @@ -25,6 +26,7 @@ sp-core = { workspace = true, features = ["std"] } [features] default = [ "std" ] std = [ + "frame-benchmarking?/std", "frame-support/std", "frame-system/std", "parity-scale-codec/std", @@ -35,6 +37,7 @@ std = [ "sp-std/std", ] runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "sp-runtime/runtime-benchmarks", diff --git a/vesting/src/benchmarking.rs b/vesting/src/benchmarking.rs new file mode 100644 index 000000000..0389b7949 --- /dev/null +++ b/vesting/src/benchmarking.rs @@ -0,0 +1,129 @@ +pub use crate::*; + +use frame_benchmarking::v2::*; +use frame_support::assert_ok; +use frame_system::RawOrigin; +use sp_std::vec; + +/// Helper trait for benchmarking. +pub trait BenchmarkHelper { + fn get_vesting_account_and_amount() -> Option<(AccountId, Balance)>; +} + +impl BenchmarkHelper for () { + fn get_vesting_account_and_amount() -> Option<(AccountId, Balance)> { + None + } +} + +fn set_balance(who: &T::AccountId, amount: BalanceOf) { + let _ = <::Currency as Currency<_>>::deposit_creating(&who, amount); +} + +fn total_balance(who: &T::AccountId) -> BalanceOf { + <::Currency as Currency<_>>::total_balance(who) +} + +fn free_balance(who: &T::AccountId) -> BalanceOf { + <::Currency as Currency<_>>::free_balance(who) +} + +#[benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn vested_transfer() { + let schedule = VestingScheduleOf:: { + start: 0u32.into(), + period: 2u32.into(), + period_count: 3u32.into(), + per_period: T::MinVestedTransfer::get(), + }; + + // extra 1 dollar to pay fees + let (from, amount) = T::BenchmarkHelper::get_vesting_account_and_amount().unwrap(); + set_balance::(&from, schedule.total_amount().unwrap() + amount); + + let to: T::AccountId = account("to", 0, 0); + let to_lookup = ::Lookup::unlookup(to.clone()); + + #[extrinsic_call] + _(RawOrigin::Signed(from), to_lookup, schedule.clone()); + + assert_eq!(total_balance::(&to), schedule.total_amount().unwrap()); + } + + #[benchmark] + fn claim(i: Linear<1, { T::MaxVestingSchedules::get() }>) { + let mut schedule = VestingScheduleOf:: { + start: 0u32.into(), + period: 2u32.into(), + period_count: 3u32.into(), + per_period: T::MinVestedTransfer::get(), + }; + + // extra 1 dollar to pay fees + let (from, amount) = T::BenchmarkHelper::get_vesting_account_and_amount().unwrap(); + set_balance::( + &from, + schedule.total_amount().unwrap().saturating_mul(i.into()) + amount, + ); + + let to: T::AccountId = account("to", 0, 0); + let to_lookup = ::Lookup::unlookup(to.clone()); + + for _ in 0..i { + schedule.start = i.into(); + assert_ok!(Pallet::::vested_transfer( + RawOrigin::Signed(from.clone()).into(), + to_lookup.clone(), + schedule.clone() + )); + } + frame_system::Pallet::::set_block_number(schedule.end().unwrap() + 1u32.into()); + + #[extrinsic_call] + _(RawOrigin::Signed(to.clone())); + + assert_eq!( + free_balance::(&to), + schedule.total_amount().unwrap().saturating_mul(i.into()), + ); + } + + #[benchmark] + fn update_vesting_schedules(i: Linear<1, { T::MaxVestingSchedules::get() }>) { + let mut schedule = VestingScheduleOf:: { + start: 0u32.into(), + period: 2u32.into(), + period_count: 3u32.into(), + per_period: T::MinVestedTransfer::get(), + }; + + let to: T::AccountId = account("to", 0, 0); + let to_lookup = ::Lookup::unlookup(to.clone()); + + set_balance::(&to, schedule.total_amount().unwrap().saturating_mul(i.into())); + + let mut schedules = vec![]; + for _ in 0..i { + schedule.start = i.into(); + schedules.push(schedule.clone()); + } + + #[extrinsic_call] + _(RawOrigin::Root, to_lookup, schedules); + + assert_eq!( + free_balance::(&to), + schedule.total_amount().unwrap().saturating_mul(i.into()), + ); + } + + impl_benchmark_test_suite! { + Pallet, + crate::mock::ExtBuilder::build(), + crate::mock::Runtime, + } +} diff --git a/vesting/src/lib.rs b/vesting/src/lib.rs index dfe6e61ce..161e2d5aa 100644 --- a/vesting/src/lib.rs +++ b/vesting/src/lib.rs @@ -45,10 +45,14 @@ use sp_std::{ vec::Vec, }; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; mod mock; mod tests; mod weights; +#[cfg(feature = "runtime-benchmarks")] +pub use benchmarking::BenchmarkHelper; pub use module::*; pub use weights::WeightInfo; @@ -143,6 +147,9 @@ pub mod module { // The block number provider type BlockNumberProvider: BlockNumberProvider>; + + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper: BenchmarkHelper>; } #[pallet::error] diff --git a/vesting/src/mock.rs b/vesting/src/mock.rs index 0e7094fa4..c96648019 100644 --- a/vesting/src/mock.rs +++ b/vesting/src/mock.rs @@ -73,6 +73,15 @@ impl BlockNumberProvider for MockBlockNumberProvider { } } +#[cfg(feature = "runtime-benchmarks")] +pub struct MockBenchmarkHelper; +#[cfg(feature = "runtime-benchmarks")] +impl BenchmarkHelper for MockBenchmarkHelper { + fn get_vesting_account_and_amount() -> Option<(AccountId, Balance)> { + Some((ALICE, 1000)) + } +} + impl Config for Runtime { type Currency = PalletBalances; type MinVestedTransfer = ConstU64<5>; @@ -80,6 +89,8 @@ impl Config for Runtime { type WeightInfo = (); type MaxVestingSchedules = ConstU32<2>; type BlockNumberProvider = MockBlockNumberProvider; + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = MockBenchmarkHelper; } type Block = frame_system::mocking::MockBlock;