From e81c1f54261e59509d950fe36119ee5fe400cbad Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 26 Mar 2026 07:36:40 +0100 Subject: [PATCH 01/10] vested payout trait --- substrate/frame/support/src/traits/tokens.rs | 3 ++- .../frame/support/src/traits/tokens/misc.rs | 25 ++++++++++++++++++ substrate/frame/vesting/src/lib.rs | 26 +++++++++++++++++++ 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/substrate/frame/support/src/traits/tokens.rs b/substrate/frame/support/src/traits/tokens.rs index be982cd31e33a..36c659c07c1bd 100644 --- a/substrate/frame/support/src/traits/tokens.rs +++ b/substrate/frame/support/src/traits/tokens.rs @@ -34,6 +34,7 @@ pub use misc::{ AssetId, Balance, BalanceStatus, ConversionFromAssetBalance, ConversionToAssetBalance, ConvertRank, DepositConsequence, ExistenceRequirement, Fortitude, GetSalary, IdAmount, Locker, Precision, Preservation, Provenance, ProvideAssetReserves, Restriction, - UnityAssetBalanceConversion, UnityOrOuterConversion, WithdrawConsequence, WithdrawReasons, + UnityAssetBalanceConversion, UnityOrOuterConversion, VestedPayout, WithdrawConsequence, + WithdrawReasons, }; pub use pay::{Pay, PayFromAccount, PayWithFungibles, PayWithSource, PaymentStatus}; diff --git a/substrate/frame/support/src/traits/tokens/misc.rs b/substrate/frame/support/src/traits/tokens/misc.rs index 9bf061793297a..731131fb7a678 100644 --- a/substrate/frame/support/src/traits/tokens/misc.rs +++ b/substrate/frame/support/src/traits/tokens/misc.rs @@ -434,3 +434,28 @@ pub struct IdAmount { /// Some amount for this item. pub amount: Balance, } + +/// Transfer `amount` from `source` to `dest` and apply a linear vesting schedule over `duration` +/// blocks starting from the current block. +/// +/// The implementor handles per-block unlock computation, block-number provider selection, and +/// the actual fund transfer internally. Callers only specify the total amount and duration. +/// +/// Unlike [`VestedTransfer`](super::currency::VestedTransfer), this trait is agnostic to both +/// the old [`Currency`](super::currency::Currency) trait and the new +/// [`fungible::Mutate`](super::fungible::Mutate) trait. The implementor (e.g. `pallet_vesting`) +/// chooses which currency mechanism to use internally, and callers do not need to provide +/// `per_block` or `starting_block` — only the total amount and vesting duration. +pub trait VestedPayout { + /// The block number type used to express vesting duration. + type BlockNumber; + + /// Transfer `amount` from `source` to `dest`, locked under a linear vesting schedule + /// spanning `duration` blocks. + fn vested_transfer( + source: &AccountId, + dest: &AccountId, + amount: Balance, + duration: Self::BlockNumber, + ) -> sp_runtime::DispatchResult; +} diff --git a/substrate/frame/vesting/src/lib.rs b/substrate/frame/vesting/src/lib.rs index 09f5f3e4431c2..b9cb921a99d10 100644 --- a/substrate/frame/vesting/src/lib.rs +++ b/substrate/frame/vesting/src/lib.rs @@ -709,6 +709,32 @@ impl Pallet { } } +impl frame_support::traits::tokens::VestedPayout> + for Pallet +where + BalanceOf: MaybeSerializeDeserialize + Debug, +{ + type BlockNumber = BlockNumberFor; + + fn vested_transfer( + source: &T::AccountId, + dest: &T::AccountId, + amount: BalanceOf, + duration: BlockNumberFor, + ) -> DispatchResult { + if duration.is_zero() { + // Zero duration means liquid transfer with no vesting schedule. + T::Currency::transfer(source, dest, amount, ExistenceRequirement::AllowDeath) + } else { + let now = T::BlockNumberProvider::current_block_number(); + let duration_as_balance = T::BlockNumberToBalance::convert(duration); + let per_block = (amount / duration_as_balance.max(One::one())).max(One::one()); + let schedule = VestingInfo::new(amount, per_block, now); + Self::do_vested_transfer(source, dest, schedule) + } + } +} + impl VestingSchedule for Pallet where BalanceOf: MaybeSerializeDeserialize + Debug, From 9f4b8254e251465c9d7b0a679ee3321853ca8003 Mon Sep 17 00:00:00 2001 From: Ankan Date: Thu, 26 Mar 2026 07:37:57 +0100 Subject: [PATCH 02/10] test for vested payout --- substrate/frame/vesting/src/tests.rs | 64 ++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/substrate/frame/vesting/src/tests.rs b/substrate/frame/vesting/src/tests.rs index aec6949f31838..958db71e30b6d 100644 --- a/substrate/frame/vesting/src/tests.rs +++ b/substrate/frame/vesting/src/tests.rs @@ -1262,3 +1262,67 @@ fn vested_transfer_impl_works() { ); }); } + +#[test] +fn vested_payout_zero_duration_is_liquid_transfer() { + use frame_support::traits::tokens::VestedPayout; + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + let alice = 3; // no vesting schedule + let bob = 4; // no vesting schedule + let alice_balance_before = Balances::free_balance(&alice); + let bob_balance_before = Balances::free_balance(&bob); + let amount = ED * 5; + + // WHEN: zero duration transfer + assert_ok!(>::vested_transfer(&alice, &bob, amount, 0)); + + // THEN: liquid transfer, no vesting schedule created. + assert_eq!(Balances::free_balance(&alice), alice_balance_before - amount); + assert_eq!(Balances::free_balance(&bob), bob_balance_before + amount); + assert!(VestingStorage::::get(&bob).is_none()); + }); +} + +#[test] +fn vested_payout_with_duration_creates_schedule() { + use frame_support::traits::tokens::VestedPayout; + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + let alice = 3; // no vesting schedule + let bob = 4; // no vesting schedule + let amount = ED * 10; + let duration = 20u64; + + // WHEN + assert_ok!(>::vested_transfer( + &alice, &bob, amount, duration + )); + + // THEN: vesting schedule is created. per_block = amount / duration = 128. + let schedule = VestingStorage::::get(&bob).unwrap(); + assert_eq!(schedule.len(), 1); + assert_eq!(schedule[0].locked(), amount); + assert_eq!(schedule[0].per_block(), amount / duration); // 128 + }); +} + +#[test] +fn vested_payout_self_transfer_creates_schedule() { + use frame_support::traits::tokens::VestedPayout; + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + let alice = 3; + let balance_before = Balances::free_balance(&alice); + let amount = ED * 5; + let duration = 10u64; + + // WHEN: self-transfer (used by staking to convert holds to vesting). + assert_ok!(>::vested_transfer( + &alice, &alice, amount, duration + )); + + // THEN: balance unchanged (self-transfer), but vesting schedule is created. + assert_eq!(Balances::free_balance(&alice), balance_before); + let schedule = VestingStorage::::get(&alice).unwrap(); + assert_eq!(schedule.len(), 1); + assert_eq!(schedule[0].locked(), amount); + }); +} From dd3bd208cb2a10d1924a1b62aceeb66d4852ee93 Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 27 Mar 2026 08:22:58 +0100 Subject: [PATCH 03/10] per block ceil and early return --- substrate/frame/vesting/src/lib.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/substrate/frame/vesting/src/lib.rs b/substrate/frame/vesting/src/lib.rs index b9cb921a99d10..49d5715ea9d43 100644 --- a/substrate/frame/vesting/src/lib.rs +++ b/substrate/frame/vesting/src/lib.rs @@ -722,13 +722,20 @@ where amount: BalanceOf, duration: BlockNumberFor, ) -> DispatchResult { + if amount.is_zero() { + return Ok(()); + } + if duration.is_zero() { // Zero duration means liquid transfer with no vesting schedule. T::Currency::transfer(source, dest, amount, ExistenceRequirement::AllowDeath) } else { let now = T::BlockNumberProvider::current_block_number(); let duration_as_balance = T::BlockNumberToBalance::convert(duration); - let per_block = (amount / duration_as_balance.max(One::one())).max(One::one()); + // Round up so that vesting completes within `duration` blocks, not longer. + let per_block = ((amount.saturating_add(duration_as_balance).saturating_sub(One::one())) / + duration_as_balance) + .max(One::one()); let schedule = VestingInfo::new(amount, per_block, now); Self::do_vested_transfer(source, dest, schedule) } From b1e8ad16da0665994007188a740f45a140698dc1 Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 27 Mar 2026 08:32:45 +0100 Subject: [PATCH 04/10] fmt --- substrate/frame/vesting/src/lib.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/substrate/frame/vesting/src/lib.rs b/substrate/frame/vesting/src/lib.rs index 49d5715ea9d43..b2130b65f7a55 100644 --- a/substrate/frame/vesting/src/lib.rs +++ b/substrate/frame/vesting/src/lib.rs @@ -733,9 +733,10 @@ where let now = T::BlockNumberProvider::current_block_number(); let duration_as_balance = T::BlockNumberToBalance::convert(duration); // Round up so that vesting completes within `duration` blocks, not longer. - let per_block = ((amount.saturating_add(duration_as_balance).saturating_sub(One::one())) / - duration_as_balance) - .max(One::one()); + let per_block = + ((amount.saturating_add(duration_as_balance).saturating_sub(One::one())) / + duration_as_balance) + .max(One::one()); let schedule = VestingInfo::new(amount, per_block, now); Self::do_vested_transfer(source, dest, schedule) } From aae97f4474df9f347c51ea62fa66f173ac201be2 Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 27 Mar 2026 08:37:38 +0100 Subject: [PATCH 05/10] tests update for rounding --- substrate/frame/vesting/src/tests.rs | 54 ++++++++++++++++++---------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/substrate/frame/vesting/src/tests.rs b/substrate/frame/vesting/src/tests.rs index 958db71e30b6d..c5fa568486461 100644 --- a/substrate/frame/vesting/src/tests.rs +++ b/substrate/frame/vesting/src/tests.rs @@ -1264,44 +1264,62 @@ fn vested_transfer_impl_works() { } #[test] -fn vested_payout_zero_duration_is_liquid_transfer() { - use frame_support::traits::tokens::VestedPayout; +fn vested_payout_edge_cases() { + use frame_support::{hypothetically, traits::tokens::VestedPayout}; ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { - let alice = 3; // no vesting schedule - let bob = 4; // no vesting schedule + let alice = 3; + let bob = 4; + let alice_balance_before = Balances::free_balance(&alice); let bob_balance_before = Balances::free_balance(&bob); - let amount = ED * 5; - // WHEN: zero duration transfer - assert_ok!(>::vested_transfer(&alice, &bob, amount, 0)); + // WHEN: zero amount, THEN: no-op. + hypothetically!({ + assert_ok!(>::vested_transfer(&alice, &bob, 0, 10)); + assert_eq!(Balances::free_balance(&bob), bob_balance_before); + assert!(VestingStorage::::get(&bob).is_none()); + }); - // THEN: liquid transfer, no vesting schedule created. - assert_eq!(Balances::free_balance(&alice), alice_balance_before - amount); - assert_eq!(Balances::free_balance(&bob), bob_balance_before + amount); - assert!(VestingStorage::::get(&bob).is_none()); + // WHEN: zero duration, THEN: liquid transfer, no vesting schedule. + hypothetically!({ + let amount = ED * 5; + assert_ok!(>::vested_transfer( + &alice, &bob, amount, 0 + )); + assert_eq!(Balances::free_balance(&alice), alice_balance_before - amount); + assert_eq!(Balances::free_balance(&bob), bob_balance_before + amount); + assert!(VestingStorage::::get(&bob).is_none()); + }); }); } #[test] -fn vested_payout_with_duration_creates_schedule() { +fn vested_payout_creates_schedule() { use frame_support::traits::tokens::VestedPayout; ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { - let alice = 3; // no vesting schedule - let bob = 4; // no vesting schedule - let amount = ED * 10; - let duration = 20u64; + let alice = 3; + let bob = 4; + + // Use amount that doesn't evenly divide by duration to test rounding. + // amount=1024, duration=30: floor division would give per_block=34, needing 31 blocks. + // Rounding up gives per_block=35, completing in 30 blocks (within duration). + let amount = ED * 4; // 1024 + let duration = 30u64; // WHEN assert_ok!(>::vested_transfer( &alice, &bob, amount, duration )); - // THEN: vesting schedule is created. per_block = amount / duration = 128. + // THEN: per_block is rounded up to 35, not floored to 34. let schedule = VestingStorage::::get(&bob).unwrap(); assert_eq!(schedule.len(), 1); assert_eq!(schedule[0].locked(), amount); - assert_eq!(schedule[0].per_block(), amount / duration); // 128 + assert_eq!(schedule[0].per_block(), 35); + + // Vesting completes within duration: ceil(1024/35) = 30 blocks <= 30. + let ending = schedule[0].ending_block_as_balance::(); + assert!(ending <= schedule[0].starting_block() + duration); }); } From 04f4a57417c2cce3982df04984b4cdcd95bee3df Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 27 Mar 2026 08:39:51 +0100 Subject: [PATCH 06/10] update rust doc --- substrate/frame/support/src/traits/tokens/misc.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/substrate/frame/support/src/traits/tokens/misc.rs b/substrate/frame/support/src/traits/tokens/misc.rs index 731131fb7a678..b067b2e628f26 100644 --- a/substrate/frame/support/src/traits/tokens/misc.rs +++ b/substrate/frame/support/src/traits/tokens/misc.rs @@ -435,8 +435,10 @@ pub struct IdAmount { pub amount: Balance, } -/// Transfer `amount` from `source` to `dest` and apply a linear vesting schedule over `duration` -/// blocks starting from the current block. +/// Transfer `amount` from `source` to `dest` and apply a linear vesting schedule that completes +/// within at most `duration` blocks starting from the current block. +/// +/// The per-block unlock rate is rounded up so that vesting never exceeds `duration` blocks. /// /// The implementor handles per-block unlock computation, block-number provider selection, and /// the actual fund transfer internally. Callers only specify the total amount and duration. @@ -451,7 +453,7 @@ pub trait VestedPayout { type BlockNumber; /// Transfer `amount` from `source` to `dest`, locked under a linear vesting schedule - /// spanning `duration` blocks. + /// that completes within at most `duration` blocks. fn vested_transfer( source: &AccountId, dest: &AccountId, From efe6e0068b616f4bdb144cb64cc48e21c169e5c5 Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 27 Mar 2026 08:52:07 +0100 Subject: [PATCH 07/10] add prdoc --- prdoc/pr_11512.prdoc | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 prdoc/pr_11512.prdoc diff --git a/prdoc/pr_11512.prdoc b/prdoc/pr_11512.prdoc new file mode 100644 index 0000000000000..cb9749de2511e --- /dev/null +++ b/prdoc/pr_11512.prdoc @@ -0,0 +1,15 @@ +title: Vested Payout trait and implementation +doc: +- audience: Runtime Dev + description: |- + Introduces a new `VestedPayout` trait in `frame-support` for transferring funds with a linear + vesting schedule. Unlike the existing `VestedTransfer` trait, callers only specify the total + amount and duration, and the implementor handles per-block computation internally. + + `pallet-vesting` provides the implementation. The per-block unlock rate is rounded up so that + vesting always completes within the specified duration, never longer. +crates: +- name: frame-support + bump: minor +- name: pallet-vesting + bump: minor From 34d96f3b7fd63ea3d2d87c5d377549b67707aa78 Mon Sep 17 00:00:00 2001 From: Ankan Date: Fri, 27 Mar 2026 08:52:42 +0100 Subject: [PATCH 08/10] fmt --- substrate/frame/vesting/src/tests.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/substrate/frame/vesting/src/tests.rs b/substrate/frame/vesting/src/tests.rs index c5fa568486461..66e89d589ba46 100644 --- a/substrate/frame/vesting/src/tests.rs +++ b/substrate/frame/vesting/src/tests.rs @@ -1283,9 +1283,7 @@ fn vested_payout_edge_cases() { // WHEN: zero duration, THEN: liquid transfer, no vesting schedule. hypothetically!({ let amount = ED * 5; - assert_ok!(>::vested_transfer( - &alice, &bob, amount, 0 - )); + assert_ok!(>::vested_transfer(&alice, &bob, amount, 0)); assert_eq!(Balances::free_balance(&alice), alice_balance_before - amount); assert_eq!(Balances::free_balance(&bob), bob_balance_before + amount); assert!(VestingStorage::::get(&bob).is_none()); From 68a3ad0422797c1e0235e6560830ba1a6ff5b153 Mon Sep 17 00:00:00 2001 From: Ankan Date: Mon, 30 Mar 2026 11:32:07 +0200 Subject: [PATCH 09/10] add future start block to vested payouts --- .../frame/support/src/traits/tokens/misc.rs | 4 ++ substrate/frame/vesting/src/lib.rs | 6 ++- substrate/frame/vesting/src/tests.rs | 40 +++++++++++++++++-- 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/substrate/frame/support/src/traits/tokens/misc.rs b/substrate/frame/support/src/traits/tokens/misc.rs index b067b2e628f26..b5f34496a3c69 100644 --- a/substrate/frame/support/src/traits/tokens/misc.rs +++ b/substrate/frame/support/src/traits/tokens/misc.rs @@ -454,10 +454,14 @@ pub trait VestedPayout { /// Transfer `amount` from `source` to `dest`, locked under a linear vesting schedule /// that completes within at most `duration` blocks. + /// + /// If `start_at` is `Some`, the vesting schedule begins at that block number; + /// otherwise it begins at the current block. fn vested_transfer( source: &AccountId, dest: &AccountId, amount: Balance, duration: Self::BlockNumber, + start_at: Option, ) -> sp_runtime::DispatchResult; } diff --git a/substrate/frame/vesting/src/lib.rs b/substrate/frame/vesting/src/lib.rs index b2130b65f7a55..06346cd176559 100644 --- a/substrate/frame/vesting/src/lib.rs +++ b/substrate/frame/vesting/src/lib.rs @@ -721,6 +721,7 @@ where dest: &T::AccountId, amount: BalanceOf, duration: BlockNumberFor, + start_at: Option>, ) -> DispatchResult { if amount.is_zero() { return Ok(()); @@ -730,14 +731,15 @@ where // Zero duration means liquid transfer with no vesting schedule. T::Currency::transfer(source, dest, amount, ExistenceRequirement::AllowDeath) } else { - let now = T::BlockNumberProvider::current_block_number(); + let starting_block = + start_at.unwrap_or_else(|| T::BlockNumberProvider::current_block_number()); let duration_as_balance = T::BlockNumberToBalance::convert(duration); // Round up so that vesting completes within `duration` blocks, not longer. let per_block = ((amount.saturating_add(duration_as_balance).saturating_sub(One::one())) / duration_as_balance) .max(One::one()); - let schedule = VestingInfo::new(amount, per_block, now); + let schedule = VestingInfo::new(amount, per_block, starting_block); Self::do_vested_transfer(source, dest, schedule) } } diff --git a/substrate/frame/vesting/src/tests.rs b/substrate/frame/vesting/src/tests.rs index 66e89d589ba46..ecc26c5c3b43d 100644 --- a/substrate/frame/vesting/src/tests.rs +++ b/substrate/frame/vesting/src/tests.rs @@ -1275,7 +1275,7 @@ fn vested_payout_edge_cases() { // WHEN: zero amount, THEN: no-op. hypothetically!({ - assert_ok!(>::vested_transfer(&alice, &bob, 0, 10)); + assert_ok!(>::vested_transfer(&alice, &bob, 0, 10, None)); assert_eq!(Balances::free_balance(&bob), bob_balance_before); assert!(VestingStorage::::get(&bob).is_none()); }); @@ -1283,11 +1283,43 @@ fn vested_payout_edge_cases() { // WHEN: zero duration, THEN: liquid transfer, no vesting schedule. hypothetically!({ let amount = ED * 5; - assert_ok!(>::vested_transfer(&alice, &bob, amount, 0)); + assert_ok!(>::vested_transfer(&alice, &bob, amount, 0, None)); assert_eq!(Balances::free_balance(&alice), alice_balance_before - amount); assert_eq!(Balances::free_balance(&bob), bob_balance_before + amount); assert!(VestingStorage::::get(&bob).is_none()); }); + + // WHEN: start_at is a future block, THEN: schedule starts at that block and + // nothing vests before it, but vesting kicks in once we reach that block. + hypothetically!({ + let amount = ED * 4; // 1024 + let duration = 10u64; + let future_block = 100u64; + assert_ok!(>::vested_transfer( + &alice, &bob, amount, duration, Some(future_block) + )); + let schedule = VestingStorage::::get(&bob).unwrap(); + assert_eq!(schedule.len(), 1); + assert_eq!(schedule[0].starting_block(), future_block); + assert_eq!(schedule[0].locked(), amount); + + // Before start_at: nothing has vested yet, full amount is still locked. + System::set_block_number(future_block - 1); + assert_eq!(Vesting::vesting_balance(&bob), Some(amount)); + + // At start_at: vesting begins, per_block amount is unlocked. + System::set_block_number(future_block); + assert_eq!(Vesting::vesting_balance(&bob), Some(amount)); + + // A few blocks after start_at: partial vesting. + System::set_block_number(future_block + 5); + let per_block = schedule[0].per_block(); + assert_eq!(Vesting::vesting_balance(&bob), Some(amount - per_block * 5)); + + // After start_at + duration: fully vested. + System::set_block_number(future_block + duration); + assert_eq!(Vesting::vesting_balance(&bob), Some(0)); + }); }); } @@ -1306,7 +1338,7 @@ fn vested_payout_creates_schedule() { // WHEN assert_ok!(>::vested_transfer( - &alice, &bob, amount, duration + &alice, &bob, amount, duration, None )); // THEN: per_block is rounded up to 35, not floored to 34. @@ -1332,7 +1364,7 @@ fn vested_payout_self_transfer_creates_schedule() { // WHEN: self-transfer (used by staking to convert holds to vesting). assert_ok!(>::vested_transfer( - &alice, &alice, amount, duration + &alice, &alice, amount, duration, None )); // THEN: balance unchanged (self-transfer), but vesting schedule is created. From 6fbcfec536372da1ef2bb9e1c89d7d3f563e9b08 Mon Sep 17 00:00:00 2001 From: Ankan Date: Mon, 30 Mar 2026 11:35:32 +0200 Subject: [PATCH 10/10] fmt --- substrate/frame/vesting/src/tests.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/substrate/frame/vesting/src/tests.rs b/substrate/frame/vesting/src/tests.rs index ecc26c5c3b43d..e00dd97180b01 100644 --- a/substrate/frame/vesting/src/tests.rs +++ b/substrate/frame/vesting/src/tests.rs @@ -1283,7 +1283,9 @@ fn vested_payout_edge_cases() { // WHEN: zero duration, THEN: liquid transfer, no vesting schedule. hypothetically!({ let amount = ED * 5; - assert_ok!(>::vested_transfer(&alice, &bob, amount, 0, None)); + assert_ok!(>::vested_transfer( + &alice, &bob, amount, 0, None + )); assert_eq!(Balances::free_balance(&alice), alice_balance_before - amount); assert_eq!(Balances::free_balance(&bob), bob_balance_before + amount); assert!(VestingStorage::::get(&bob).is_none()); @@ -1296,7 +1298,11 @@ fn vested_payout_edge_cases() { let duration = 10u64; let future_block = 100u64; assert_ok!(>::vested_transfer( - &alice, &bob, amount, duration, Some(future_block) + &alice, + &bob, + amount, + duration, + Some(future_block) )); let schedule = VestingStorage::::get(&bob).unwrap(); assert_eq!(schedule.len(), 1);