From c68672182b1d4be15fe6920b5ade9b465d3736b3 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Wed, 20 Jul 2022 13:55:56 +0300 Subject: [PATCH 01/24] Define VestingSchedule trait --- Cargo.lock | 7 +++++++ crates/vesting-schedule/Cargo.toml | 14 ++++++++++++++ crates/vesting-schedule/src/lib.rs | 4 ++++ crates/vesting-schedule/src/traits.rs | 22 ++++++++++++++++++++++ 4 files changed, 47 insertions(+) create mode 100644 crates/vesting-schedule/Cargo.toml create mode 100644 crates/vesting-schedule/src/lib.rs create mode 100644 crates/vesting-schedule/src/traits.rs diff --git a/Cargo.lock b/Cargo.lock index 067fc101c..a6f70cd9b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10038,6 +10038,13 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "vesting-schedule" +version = "0.1.0" +dependencies = [ + "frame-support", +] + [[package]] name = "void" version = "1.0.2" diff --git a/crates/vesting-schedule/Cargo.toml b/crates/vesting-schedule/Cargo.toml new file mode 100644 index 000000000..0712bad8e --- /dev/null +++ b/crates/vesting-schedule/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "vesting-schedule" +version = "0.1.0" +edition = "2021" +publish = false + +[dependencies] +frame-support = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "master" } + +[features] +default = ["std"] +std = [ + "frame-support/std", +] diff --git a/crates/vesting-schedule/src/lib.rs b/crates/vesting-schedule/src/lib.rs new file mode 100644 index 000000000..2eacfd611 --- /dev/null +++ b/crates/vesting-schedule/src/lib.rs @@ -0,0 +1,4 @@ +//! The vesting schedule. + +mod traits; +pub use traits::*; diff --git a/crates/vesting-schedule/src/traits.rs b/crates/vesting-schedule/src/traits.rs new file mode 100644 index 000000000..ea2b21358 --- /dev/null +++ b/crates/vesting-schedule/src/traits.rs @@ -0,0 +1,22 @@ +//! Generic vesting schedule related traits to abstract away the implementations. + +use frame_support::traits::Currency; + +/// [`VestingSchedule`] defines logic for currency vesting(unlocking). +pub trait VestingSchedule { + /// The type used to denote time: Timestamp, BlockNumber, etc. + type Moment; + /// The currency that this schedule applies to. + type Currency: Currency; + /// Locked amount at provided moment. + fn locked_at( + genesis_locked: >::Balance, + start: Self::Moment, + moment: Self::Moment, + ) -> >::Balance; + /// Moment at which the schedule ends. + fn end( + genesis_locked: >::Balance, + start: Self::Moment, + ) -> Self::Moment; +} From 7857a38230638bfbdb738bb8d19c7f99cf0abc27 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Wed, 20 Jul 2022 17:17:48 +0300 Subject: [PATCH 02/24] Implement locked_at --- Cargo.lock | 1 + crates/vesting-schedule/Cargo.toml | 2 + crates/vesting-schedule/src/lib.rs | 59 +++++++++++++++++++++++++++ crates/vesting-schedule/src/traits.rs | 2 + 4 files changed, 64 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index a6f70cd9b..786d2d414 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10043,6 +10043,7 @@ name = "vesting-schedule" version = "0.1.0" dependencies = [ "frame-support", + "sp-arithmetic", ] [[package]] diff --git a/crates/vesting-schedule/Cargo.toml b/crates/vesting-schedule/Cargo.toml index 0712bad8e..0c3d98025 100644 --- a/crates/vesting-schedule/Cargo.toml +++ b/crates/vesting-schedule/Cargo.toml @@ -6,9 +6,11 @@ publish = false [dependencies] frame-support = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "master" } +sp-arithmetic = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "master" } [features] default = ["std"] std = [ "frame-support/std", + "sp-arithmetic/std", ] diff --git a/crates/vesting-schedule/src/lib.rs b/crates/vesting-schedule/src/lib.rs index 2eacfd611..802d0d16f 100644 --- a/crates/vesting-schedule/src/lib.rs +++ b/crates/vesting-schedule/src/lib.rs @@ -1,4 +1,63 @@ //! The vesting schedule. +use frame_support::traits::Currency as CurrencyT; +use sp_arithmetic::traits::{ + AtLeast32BitUnsigned, CheckedMul, Saturating, UniqueSaturatedFrom, UniqueSaturatedInto, Zero, +}; + mod traits; pub use traits::*; + +pub struct LinearWithCliff> { + cliff: Moment, + period: Moment, + per_period: Currency::Balance, +} + +impl VestingSchedule + for LinearWithCliff +where + Currency: CurrencyT, + Moment: AtLeast32BitUnsigned + Copy, +{ + type Moment = Moment; + + type Currency = Currency; + + fn locked_at( + &self, + genesis_locked: Currency::Balance, + start: Self::Moment, + moment: Self::Moment, + ) -> Currency::Balance { + let actual_start = start.saturating_add(self.cliff); + if actual_start > moment { + return genesis_locked; + } + + let actual_end = self.end(genesis_locked, start); + if actual_end < moment { + return Zero::zero(); + } + + let actual_vesting_time = moment.saturating_sub(actual_start); + let periods_number = >::unique_saturated_into( + actual_vesting_time + .checked_div(&self.period) + .unwrap_or_else(Zero::zero), + ); + let vested_balance = self + .per_period + .checked_mul( + &>::unique_saturated_from( + periods_number, + ), + ) + .unwrap(); + genesis_locked.saturating_sub(vested_balance) + } + + fn end(&self, genesis_locked: Currency::Balance, start: Self::Moment) -> Self::Moment { + todo!() + } +} diff --git a/crates/vesting-schedule/src/traits.rs b/crates/vesting-schedule/src/traits.rs index ea2b21358..6ebbefbda 100644 --- a/crates/vesting-schedule/src/traits.rs +++ b/crates/vesting-schedule/src/traits.rs @@ -10,12 +10,14 @@ pub trait VestingSchedule { type Currency: Currency; /// Locked amount at provided moment. fn locked_at( + &self, genesis_locked: >::Balance, start: Self::Moment, moment: Self::Moment, ) -> >::Balance; /// Moment at which the schedule ends. fn end( + &self, genesis_locked: >::Balance, start: Self::Moment, ) -> Self::Moment; From 5d14f88aaae899e455ea9b42e00566cf01e81b13 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Wed, 20 Jul 2022 17:21:17 +0300 Subject: [PATCH 03/24] Add docs to LinearWithCliff --- crates/vesting-schedule/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/vesting-schedule/src/lib.rs b/crates/vesting-schedule/src/lib.rs index 802d0d16f..82dfdccde 100644 --- a/crates/vesting-schedule/src/lib.rs +++ b/crates/vesting-schedule/src/lib.rs @@ -8,9 +8,13 @@ use sp_arithmetic::traits::{ mod traits; pub use traits::*; +/// Implements linear westing logic with cliff. pub struct LinearWithCliff> { + /// Vesting cliff. cliff: Moment, + /// Vesting period. period: Moment, + /// Amount that should be unlocked per one vesting period. per_period: Currency::Balance, } From b95b1df6f121701c0188ff653ca1f1ba15dbc0fb Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Wed, 20 Jul 2022 17:34:14 +0300 Subject: [PATCH 04/24] Implement end --- crates/vesting-schedule/src/lib.rs | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/crates/vesting-schedule/src/lib.rs b/crates/vesting-schedule/src/lib.rs index 82dfdccde..b9c873637 100644 --- a/crates/vesting-schedule/src/lib.rs +++ b/crates/vesting-schedule/src/lib.rs @@ -2,7 +2,8 @@ use frame_support::traits::Currency as CurrencyT; use sp_arithmetic::traits::{ - AtLeast32BitUnsigned, CheckedMul, Saturating, UniqueSaturatedFrom, UniqueSaturatedInto, Zero, + AtLeast32BitUnsigned, CheckedDiv, CheckedMul, Saturating, UniqueSaturatedFrom, + UniqueSaturatedInto, Zero, }; mod traits; @@ -57,11 +58,30 @@ where periods_number, ), ) - .unwrap(); + .unwrap_or_else(Zero::zero); genesis_locked.saturating_sub(vested_balance) } fn end(&self, genesis_locked: Currency::Balance, start: Self::Moment) -> Self::Moment { - todo!() + let periods_number = >::unique_saturated_into( + genesis_locked + .checked_div(&self.per_period) + .unwrap_or_else(Zero::zero), + ) + if (genesis_locked % self.per_period).is_zero() { + 0 + } else { + // `per_period` does not perfectly divide `locked`, so we need an extra period to + // unlock some amount less than `per_period`. + 1 + }; + + let actual_start = start.saturating_add(self.cliff); + let actual_vesting_time = self + .period + .checked_mul( + &>::unique_saturated_from(periods_number), + ) + .unwrap_or_else(Zero::zero); + actual_start.saturating_add(actual_vesting_time) } } From ccfeca81a7834732282b1f669e03d3187fd94b61 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Wed, 20 Jul 2022 17:39:45 +0300 Subject: [PATCH 05/24] Add little note --- crates/vesting-schedule/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/vesting-schedule/src/lib.rs b/crates/vesting-schedule/src/lib.rs index b9c873637..8b3ed651b 100644 --- a/crates/vesting-schedule/src/lib.rs +++ b/crates/vesting-schedule/src/lib.rs @@ -15,7 +15,7 @@ pub struct LinearWithCliff> { cliff: Moment, /// Vesting period. period: Moment, - /// Amount that should be unlocked per one vesting period. + /// Amount that should be unlocked per one vesting period. (!= 0) per_period: Currency::Balance, } From c98e14d381c7c82af21318b8e3b95dd1aae66840 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 21 Jul 2022 14:20:42 +0300 Subject: [PATCH 06/24] Add validate logic --- crates/vesting-schedule/src/lib.rs | 15 +++++++++++++++ crates/vesting-schedule/src/traits.rs | 4 ++++ 2 files changed, 19 insertions(+) diff --git a/crates/vesting-schedule/src/lib.rs b/crates/vesting-schedule/src/lib.rs index 8b3ed651b..dfcf7b2cd 100644 --- a/crates/vesting-schedule/src/lib.rs +++ b/crates/vesting-schedule/src/lib.rs @@ -19,6 +19,12 @@ pub struct LinearWithCliff> { per_period: Currency::Balance, } +/// An error that can occur at linear with cliff vesting schedule logic. +pub enum LinearWithCliffError { + /// We don't let `per_period` be less than 1, or else the vesting will never end. + ZeroPerPeriod, +} + impl VestingSchedule for LinearWithCliff where @@ -29,6 +35,15 @@ where type Currency = Currency; + type Error = LinearWithCliffError; + + fn validate(&self) -> Result<(), Self::Error> { + if self.per_period == Zero::zero() { + return Err(LinearWithCliffError::ZeroPerPeriod); + } + Ok(()) + } + fn locked_at( &self, genesis_locked: Currency::Balance, diff --git a/crates/vesting-schedule/src/traits.rs b/crates/vesting-schedule/src/traits.rs index 6ebbefbda..03e796179 100644 --- a/crates/vesting-schedule/src/traits.rs +++ b/crates/vesting-schedule/src/traits.rs @@ -8,6 +8,10 @@ pub trait VestingSchedule { type Moment; /// The currency that this schedule applies to. type Currency: Currency; + /// An error that can occur at vesting schedule logic. + type Error; + /// Validate the schedule. + fn validate(&self) -> Result<(), Self::Error>; /// Locked amount at provided moment. fn locked_at( &self, From 4b5566cb5fcb3d107314f67f69a9a731f21edc00 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Mon, 25 Jul 2022 12:24:12 +0300 Subject: [PATCH 07/24] Enable no_std mod --- crates/vesting-schedule/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/vesting-schedule/src/lib.rs b/crates/vesting-schedule/src/lib.rs index dfcf7b2cd..ac818f03b 100644 --- a/crates/vesting-schedule/src/lib.rs +++ b/crates/vesting-schedule/src/lib.rs @@ -1,5 +1,7 @@ //! The vesting schedule. +#![cfg_attr(not(feature = "std"), no_std)] + use frame_support::traits::Currency as CurrencyT; use sp_arithmetic::traits::{ AtLeast32BitUnsigned, CheckedDiv, CheckedMul, Saturating, UniqueSaturatedFrom, From 14ab8fa7eee6c289a1a9b948f4384d066ce497a8 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Tue, 26 Jul 2022 19:54:20 +0300 Subject: [PATCH 08/24] Improve validate logic for LinearWithCliff --- crates/vesting-schedule/src/lib.rs | 11 ++++++++++- crates/vesting-schedule/src/traits.rs | 6 +++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/crates/vesting-schedule/src/lib.rs b/crates/vesting-schedule/src/lib.rs index ac818f03b..9c4345394 100644 --- a/crates/vesting-schedule/src/lib.rs +++ b/crates/vesting-schedule/src/lib.rs @@ -25,6 +25,8 @@ pub struct LinearWithCliff> { pub enum LinearWithCliffError { /// We don't let `per_period` be less than 1, or else the vesting will never end. ZeroPerPeriod, + /// Genesis locked shouldn't be zero. + ZeroGenesisLocked, } impl VestingSchedule @@ -39,10 +41,17 @@ where type Error = LinearWithCliffError; - fn validate(&self) -> Result<(), Self::Error> { + fn validate( + &self, + genesis_locked: Currency::Balance, + _start: Self::Moment, + ) -> Result<(), Self::Error> { if self.per_period == Zero::zero() { return Err(LinearWithCliffError::ZeroPerPeriod); } + if genesis_locked == Zero::zero() { + return Err(LinearWithCliffError::ZeroGenesisLocked); + } Ok(()) } diff --git a/crates/vesting-schedule/src/traits.rs b/crates/vesting-schedule/src/traits.rs index 03e796179..d295d3898 100644 --- a/crates/vesting-schedule/src/traits.rs +++ b/crates/vesting-schedule/src/traits.rs @@ -11,7 +11,11 @@ pub trait VestingSchedule { /// An error that can occur at vesting schedule logic. type Error; /// Validate the schedule. - fn validate(&self) -> Result<(), Self::Error>; + fn validate( + &self, + genesis_locked: >::Balance, + start: Self::Moment, + ) -> Result<(), Self::Error>; /// Locked amount at provided moment. fn locked_at( &self, From 3b015c4b11decd507e0fa25cba028640a1c0a68e Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Fri, 22 Jul 2022 14:46:58 +0300 Subject: [PATCH 09/24] Define vesting logic --- Cargo.lock | 8 +++++++ crates/vesting/Cargo.toml | 17 +++++++++++++ crates/vesting/src/lib.rs | 4 ++++ crates/vesting/src/traits.rs | 46 ++++++++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+) create mode 100644 crates/vesting/Cargo.toml create mode 100644 crates/vesting/src/lib.rs create mode 100644 crates/vesting/src/traits.rs diff --git a/Cargo.lock b/Cargo.lock index 94f43a8af..e3d86b6fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10163,6 +10163,14 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "vesting" +version = "0.1.0" +dependencies = [ + "frame-support", + "vesting-schedule", +] + [[package]] name = "vesting-schedule" version = "0.1.0" diff --git a/crates/vesting/Cargo.toml b/crates/vesting/Cargo.toml new file mode 100644 index 000000000..ffa82dcbd --- /dev/null +++ b/crates/vesting/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "vesting" +version = "0.1.0" +edition = "2021" +publish = false + +[dependencies] +vesting-schedule = { version = "0.1", path = "../vesting-schedule", default-features = false } + +frame-support = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "master" } + +[features] +default = ["std"] +std = [ + "frame-support/std", + "vesting-schedule/std", +] diff --git a/crates/vesting/src/lib.rs b/crates/vesting/src/lib.rs new file mode 100644 index 000000000..3bf2c45f2 --- /dev/null +++ b/crates/vesting/src/lib.rs @@ -0,0 +1,4 @@ +//! The vesting. + +mod traits; +pub use traits::*; diff --git a/crates/vesting/src/traits.rs b/crates/vesting/src/traits.rs new file mode 100644 index 000000000..92af92942 --- /dev/null +++ b/crates/vesting/src/traits.rs @@ -0,0 +1,46 @@ +//! Generic vesting related traits to abstract away the implementations. + +use frame_support::{dispatch::DispatchResult, traits::Currency}; +use vesting_schedule::VestingSchedule; + +/// A general vesting logic. +pub trait Vesting { + /// Defines logic of vesting schedule to be used. + type VestingSchedule: VestingSchedule; + + /// Get the amount that is currently being vested and cannot be transferred out of this account. + /// Returns `None` if the account has no vesting schedule. + fn vesting_balance( + who: &AccountId, + ) -> Option< + <>::Currency as Currency>::Balance, + >; + + /// Adds a vesting schedule to a given account. + /// + /// If the account has `MaxVestingSchedules`, an Error is returned and nothing + /// is updated. + /// + /// Is a no-op if the amount to be vested is zero. + fn add_vesting_schedule( + who: &AccountId, + locked: <>::Currency as Currency< + AccountId, + >>::Balance, + start: >::Moment, + vesting_schedule: Self::VestingSchedule, + ) -> DispatchResult; + + /// Checks if `add_vesting_schedule` would work against `who`. + fn can_add_vesting_schedule( + who: &AccountId, + locked: <>::Currency as Currency< + AccountId, + >>::Balance, + start: >::Moment, + vesting_schedule: Self::VestingSchedule, + ) -> DispatchResult; + + /// Remove a vesting schedule for a given account. + fn remove_vesting_schedule(who: &AccountId, schedule_index: u32) -> DispatchResult; +} From 0f811bcb3cb3817ac209b315124e976e414ee941 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Fri, 22 Jul 2022 15:23:11 +0300 Subject: [PATCH 10/24] Rename to avoid multiple versions --- Cargo.lock | 2 +- crates/{vesting => vesting-logic}/Cargo.toml | 2 +- crates/{vesting => vesting-logic}/src/lib.rs | 0 crates/{vesting => vesting-logic}/src/traits.rs | 0 4 files changed, 2 insertions(+), 2 deletions(-) rename crates/{vesting => vesting-logic}/Cargo.toml (94%) rename crates/{vesting => vesting-logic}/src/lib.rs (100%) rename crates/{vesting => vesting-logic}/src/traits.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index e3d86b6fb..0ed5ed215 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10164,7 +10164,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] -name = "vesting" +name = "vesting-logic" version = "0.1.0" dependencies = [ "frame-support", diff --git a/crates/vesting/Cargo.toml b/crates/vesting-logic/Cargo.toml similarity index 94% rename from crates/vesting/Cargo.toml rename to crates/vesting-logic/Cargo.toml index ffa82dcbd..fc539ddf3 100644 --- a/crates/vesting/Cargo.toml +++ b/crates/vesting-logic/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "vesting" +name = "vesting-logic" version = "0.1.0" edition = "2021" publish = false diff --git a/crates/vesting/src/lib.rs b/crates/vesting-logic/src/lib.rs similarity index 100% rename from crates/vesting/src/lib.rs rename to crates/vesting-logic/src/lib.rs diff --git a/crates/vesting/src/traits.rs b/crates/vesting-logic/src/traits.rs similarity index 100% rename from crates/vesting/src/traits.rs rename to crates/vesting-logic/src/traits.rs From 458d760edd47567e598498f9881039d38de50c7d Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Mon, 25 Jul 2022 12:30:39 +0300 Subject: [PATCH 11/24] Enable no_std mod --- crates/vesting-logic/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/vesting-logic/src/lib.rs b/crates/vesting-logic/src/lib.rs index 3bf2c45f2..dace4bba3 100644 --- a/crates/vesting-logic/src/lib.rs +++ b/crates/vesting-logic/src/lib.rs @@ -1,4 +1,6 @@ //! The vesting. +#![cfg_attr(not(feature = "std"), no_std)] + mod traits; pub use traits::*; From 5fa55878f04ca3b3b68cf5e19f897da8b070062f Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Tue, 26 Jul 2022 14:01:59 +0300 Subject: [PATCH 12/24] Define Config trait --- Cargo.lock | 14 ++++++++ crates/pallet-vesting/Cargo.toml | 29 ++++++++++++++++ crates/pallet-vesting/src/lib.rs | 57 ++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 crates/pallet-vesting/Cargo.toml create mode 100644 crates/pallet-vesting/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 0ed5ed215..f62d32eaf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5689,6 +5689,20 @@ dependencies = [ "sp-runtime", ] +[[package]] +name = "pallet-vesting" +version = "0.1.0" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "serde", + "sp-runtime", + "sp-std", + "vesting-schedule", +] + [[package]] name = "parity-db" version = "0.3.13" diff --git a/crates/pallet-vesting/Cargo.toml b/crates/pallet-vesting/Cargo.toml new file mode 100644 index 000000000..aeacc75cf --- /dev/null +++ b/crates/pallet-vesting/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "pallet-vesting" +version = "0.1.0" +edition = "2021" +publish = false + +[dependencies] +vesting-schedule = { version = "0.1", path = "../vesting-schedule", default-features = false } + +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +frame-support = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "master" } +frame-system = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "master" } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +serde = { version = "1", features = ["derive"], optional = true } +sp-runtime = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "master" } +sp-std = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "master" } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "serde", + "sp-runtime/std", + "sp-std/std", +] +try-runtime = ["frame-support/try-runtime"] diff --git a/crates/pallet-vesting/src/lib.rs b/crates/pallet-vesting/src/lib.rs new file mode 100644 index 000000000..23cbfd093 --- /dev/null +++ b/crates/pallet-vesting/src/lib.rs @@ -0,0 +1,57 @@ +//! The vesting pallet. + +#![cfg_attr(not(feature = "std"), no_std)] + +use codec::MaxEncodedLen; +use frame_support::{ + pallet_prelude::*, + traits::{Currency, StorageVersion}, +}; +pub use pallet::*; +use sp_runtime::traits::{AtLeast32BitUnsigned, MaybeSerializeDeserialize}; +use vesting_schedule::VestingSchedule; + +/// The current storage version. +const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); + +/// Provides the capability to get current moment. +pub trait CurrentMoment { + /// Return current moment. + fn now() -> Moment; +} + +// We have to temporarily allow some clippy lints. Later on we'll send patches to substrate to +// fix them at their end. +#[allow(clippy::missing_docs_in_private_items)] +#[frame_support::pallet] +pub mod pallet { + use super::*; + + /// Configure the pallet by specifying the parameters and types on which it depends. + #[pallet::config] + pub trait Config: frame_system::Config { + /// The currency to operate with. + type Currency: Currency; + + /// The vesting schedule to operate with. + type VestingSchedule: VestingSchedule; + + /// Type used for expressing moment. + type Moment: Parameter + + Default + + AtLeast32BitUnsigned + + Copy + + MaybeSerializeDeserialize + + MaxEncodedLen; + + /// The getter for the current moment. + type CurrentMoment: CurrentMoment; + + /// Maximum number of vesting schedules an account may have at a given moment. + const MAX_VESTING_SCHEDULES: u32; + } + + #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] + pub struct Pallet(_); +} From bc5432838e243adb1151a193a0bde34d838d0d37 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Tue, 26 Jul 2022 14:39:46 +0300 Subject: [PATCH 13/24] Add Vesting storage as information regarding the vesting of a given account --- crates/pallet-vesting/src/lib.rs | 20 +++++++++++++++++++- crates/pallet-vesting/src/vesting_info.rs | 12 ++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 crates/pallet-vesting/src/vesting_info.rs diff --git a/crates/pallet-vesting/src/lib.rs b/crates/pallet-vesting/src/lib.rs index 23cbfd093..b36903e2a 100644 --- a/crates/pallet-vesting/src/lib.rs +++ b/crates/pallet-vesting/src/lib.rs @@ -2,15 +2,23 @@ #![cfg_attr(not(feature = "std"), no_std)] +mod vesting_info; + use codec::MaxEncodedLen; use frame_support::{ pallet_prelude::*, + storage::bounded_vec::BoundedVec, traits::{Currency, StorageVersion}, }; pub use pallet::*; use sp_runtime::traits::{AtLeast32BitUnsigned, MaybeSerializeDeserialize}; +use vesting_info::VestingInfo; use vesting_schedule::VestingSchedule; +/// Balance type alias. +type BalanceOf = + <::Currency as Currency<::AccountId>>::Balance; + /// The current storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); @@ -48,10 +56,20 @@ pub mod pallet { type CurrentMoment: CurrentMoment; /// Maximum number of vesting schedules an account may have at a given moment. - const MAX_VESTING_SCHEDULES: u32; + type MaxVestingSchedules: Get; } #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); + + /// Information regarding the vesting of a given account. + #[pallet::storage] + #[pallet::getter(fn vesting)] + pub type Vesting = StorageMap< + _, + Blake2_128Concat, + T::AccountId, + BoundedVec, ::Moment>, T::MaxVestingSchedules>, + >; } diff --git a/crates/pallet-vesting/src/vesting_info.rs b/crates/pallet-vesting/src/vesting_info.rs new file mode 100644 index 000000000..4df6ddab8 --- /dev/null +++ b/crates/pallet-vesting/src/vesting_info.rs @@ -0,0 +1,12 @@ +//! VestingInfo. + +use super::*; + +/// Struct to encode the vesting schedule of an individual account. +#[derive(Encode, Decode, Copy, Clone, PartialEq, Eq, RuntimeDebug, MaxEncodedLen, TypeInfo)] +pub struct VestingInfo { + /// Locked amount at genesis. + locked: Balance, + /// Starting moment for unlocking(vesting). + start: Moment, +} From ece65a8d94419d886b14aadc1f9dbc9bb0c349ec Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Tue, 26 Jul 2022 17:14:08 +0300 Subject: [PATCH 14/24] Avoid complex type --- crates/pallet-vesting/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/pallet-vesting/src/lib.rs b/crates/pallet-vesting/src/lib.rs index b36903e2a..a7a4b3679 100644 --- a/crates/pallet-vesting/src/lib.rs +++ b/crates/pallet-vesting/src/lib.rs @@ -18,6 +18,8 @@ use vesting_schedule::VestingSchedule; /// Balance type alias. type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; +/// Full VestingInfo type. +type FullVestingInfo = VestingInfo, ::Moment>; /// The current storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); @@ -70,6 +72,6 @@ pub mod pallet { _, Blake2_128Concat, T::AccountId, - BoundedVec, ::Moment>, T::MaxVestingSchedules>, + BoundedVec, T::MaxVestingSchedules>, >; } From bb4b15ce703b2cc7cec8025c4701f244e8fe3e8b Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Tue, 26 Jul 2022 19:49:07 +0300 Subject: [PATCH 15/24] Add GenesisBuild --- crates/pallet-vesting/src/lib.rs | 47 +++++++++++++++++++++-- crates/pallet-vesting/src/vesting_info.rs | 4 +- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/crates/pallet-vesting/src/lib.rs b/crates/pallet-vesting/src/lib.rs index a7a4b3679..01fcd3d7f 100644 --- a/crates/pallet-vesting/src/lib.rs +++ b/crates/pallet-vesting/src/lib.rs @@ -8,10 +8,10 @@ use codec::MaxEncodedLen; use frame_support::{ pallet_prelude::*, storage::bounded_vec::BoundedVec, - traits::{Currency, StorageVersion}, + traits::{Currency, LockIdentifier, LockableCurrency, StorageVersion, WithdrawReasons}, }; pub use pallet::*; -use sp_runtime::traits::{AtLeast32BitUnsigned, MaybeSerializeDeserialize}; +use sp_runtime::traits::{AtLeast32BitUnsigned, MaybeSerializeDeserialize, Zero}; use vesting_info::VestingInfo; use vesting_schedule::VestingSchedule; @@ -41,7 +41,7 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { /// The currency to operate with. - type Currency: Currency; + type Currency: LockableCurrency; /// The vesting schedule to operate with. type VestingSchedule: VestingSchedule; @@ -59,6 +59,9 @@ pub mod pallet { /// Maximum number of vesting schedules an account may have at a given moment. type MaxVestingSchedules: Get; + + /// An lock identifier for a lockable currency to be used in vesting. + type VestingLockId: Get; } #[pallet::pallet] @@ -74,4 +77,42 @@ pub mod pallet { T::AccountId, BoundedVec, T::MaxVestingSchedules>, >; + + #[pallet::genesis_config] + pub struct GenesisConfig { + /// The list of vesting to use. + pub vesting: Vec<(T::AccountId, BalanceOf, T::Moment)>, + } + + #[cfg(feature = "std")] + impl Default for GenesisConfig { + fn default() -> Self { + GenesisConfig { + vesting: Default::default(), + } + } + } + + #[pallet::genesis_build] + impl GenesisBuild for GenesisConfig { + fn build(&self) { + for &(ref who, locked, start) in self.vesting.iter() { + let balance = T::Currency::free_balance(who); + assert!( + !balance.is_zero(), + "Currencies must be init'd before vesting" + ); + + // TODO. Vesting validate + + let vesting_info = VestingInfo { locked, start }; + + Vesting::::try_append(who, vesting_info) + .expect("Too many vesting schedules at genesis."); + + let reasons = WithdrawReasons::TRANSFER | WithdrawReasons::RESERVE; + T::Currency::set_lock(T::VestingLockId::get(), who, locked, reasons); + } + } + } } diff --git a/crates/pallet-vesting/src/vesting_info.rs b/crates/pallet-vesting/src/vesting_info.rs index 4df6ddab8..da23c877b 100644 --- a/crates/pallet-vesting/src/vesting_info.rs +++ b/crates/pallet-vesting/src/vesting_info.rs @@ -6,7 +6,7 @@ use super::*; #[derive(Encode, Decode, Copy, Clone, PartialEq, Eq, RuntimeDebug, MaxEncodedLen, TypeInfo)] pub struct VestingInfo { /// Locked amount at genesis. - locked: Balance, + pub locked: Balance, /// Starting moment for unlocking(vesting). - start: Moment, + pub start: Moment, } From c7f16fcbe234e0300ce8273240429d9e063bb24c Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Wed, 27 Jul 2022 13:36:34 +0300 Subject: [PATCH 16/24] Fix typo --- crates/pallet-vesting/src/lib.rs | 2 +- crates/vesting-schedule/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/pallet-vesting/src/lib.rs b/crates/pallet-vesting/src/lib.rs index 01fcd3d7f..a846f6065 100644 --- a/crates/pallet-vesting/src/lib.rs +++ b/crates/pallet-vesting/src/lib.rs @@ -103,7 +103,7 @@ pub mod pallet { "Currencies must be init'd before vesting" ); - // TODO. Vesting validate + VestingSchedule::validate(::VestingSchedule, locked, start); let vesting_info = VestingInfo { locked, start }; diff --git a/crates/vesting-schedule/src/lib.rs b/crates/vesting-schedule/src/lib.rs index 9c4345394..4865b5ead 100644 --- a/crates/vesting-schedule/src/lib.rs +++ b/crates/vesting-schedule/src/lib.rs @@ -11,7 +11,7 @@ use sp_arithmetic::traits::{ mod traits; pub use traits::*; -/// Implements linear westing logic with cliff. +/// Implements linear vesting logic with cliff. pub struct LinearWithCliff> { /// Vesting cliff. cliff: Moment, From 7d4decfa288800f3ffc17b96c9cc485fee218363 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 28 Jul 2022 13:40:05 +0300 Subject: [PATCH 17/24] Move vesting-schedule tp pallet-vestig as mod --- Cargo.lock | 3 +- crates/humanode-runtime/Cargo.toml | 3 ++ crates/pallet-vesting/Cargo.toml | 4 +-- crates/pallet-vesting/src/lib.rs | 16 ++++++--- .../src/vesting_schedule.rs} | 34 ++++++++++++++++--- crates/vesting-schedule/Cargo.toml | 16 --------- crates/vesting-schedule/src/traits.rs | 32 ----------------- 7 files changed, 48 insertions(+), 60 deletions(-) rename crates/{vesting-schedule/src/lib.rs => pallet-vesting/src/vesting_schedule.rs} (76%) delete mode 100644 crates/vesting-schedule/Cargo.toml delete mode 100644 crates/vesting-schedule/src/traits.rs diff --git a/Cargo.lock b/Cargo.lock index f62d32eaf..da9ad5d01 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3220,6 +3220,7 @@ dependencies = [ "pallet-timestamp", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", + "pallet-vesting", "parity-scale-codec", "precompile-bioauth", "precompile-evm-accounts-mapping", @@ -5698,9 +5699,9 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", + "sp-arithmetic", "sp-runtime", "sp-std", - "vesting-schedule", ] [[package]] diff --git a/crates/humanode-runtime/Cargo.toml b/crates/humanode-runtime/Cargo.toml index 6ea817eff..adba0d04c 100644 --- a/crates/humanode-runtime/Cargo.toml +++ b/crates/humanode-runtime/Cargo.toml @@ -20,6 +20,7 @@ pallet-ethereum-chain-id = { version = "0.1", path = "../pallet-ethereum-chain-i pallet-evm-accounts-mapping = { version = "0.1", path = "../pallet-evm-accounts-mapping", default-features = false } pallet-humanode-session = { version = "0.1", path = "../pallet-humanode-session", default-features = false } pallet-pot = { version = "0.1", path = "../pallet-pot", default-features = false } +pallet-vesting = { version = "0.1", path = "../pallet-vesting", default-features = false } precompile-bioauth = { version = "0.1", path = "../precompile-bioauth", default-features = false } precompile-evm-accounts-mapping = { version = "0.1", path = "../precompile-evm-accounts-mapping", default-features = false } primitives-auth-ticket = { version = "0.1", path = "../primitives-auth-ticket", default-features = false } @@ -134,6 +135,7 @@ std = [ "pallet-timestamp/std", "pallet-transaction-payment/std", "pallet-transaction-payment-rpc-runtime-api/std", + "pallet-vesting/std", "robonode-crypto/std", "sp-application-crypto/std", "sp-api/std", @@ -171,4 +173,5 @@ try-runtime = [ "pallet-sudo/try-runtime", "pallet-timestamp/try-runtime", "pallet-transaction-payment/try-runtime", + "pallet-vesting/try-runtime", ] diff --git a/crates/pallet-vesting/Cargo.toml b/crates/pallet-vesting/Cargo.toml index aeacc75cf..530887d29 100644 --- a/crates/pallet-vesting/Cargo.toml +++ b/crates/pallet-vesting/Cargo.toml @@ -5,13 +5,12 @@ edition = "2021" publish = false [dependencies] -vesting-schedule = { version = "0.1", path = "../vesting-schedule", default-features = false } - codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } frame-support = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "master" } frame-system = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "master" } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1", features = ["derive"], optional = true } +sp-arithmetic = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "master" } sp-runtime = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "master" } sp-std = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "master" } @@ -23,6 +22,7 @@ std = [ "frame-system/std", "scale-info/std", "serde", + "sp-arithmetic/std", "sp-runtime/std", "sp-std/std", ] diff --git a/crates/pallet-vesting/src/lib.rs b/crates/pallet-vesting/src/lib.rs index a846f6065..8970c9de4 100644 --- a/crates/pallet-vesting/src/lib.rs +++ b/crates/pallet-vesting/src/lib.rs @@ -3,6 +3,7 @@ #![cfg_attr(not(feature = "std"), no_std)] mod vesting_info; +mod vesting_schedule; use codec::MaxEncodedLen; use frame_support::{ @@ -43,9 +44,6 @@ pub mod pallet { /// The currency to operate with. type Currency: LockableCurrency; - /// The vesting schedule to operate with. - type VestingSchedule: VestingSchedule; - /// Type used for expressing moment. type Moment: Parameter + Default @@ -54,6 +52,16 @@ pub mod pallet { + MaybeSerializeDeserialize + MaxEncodedLen; + /// The vesting schedule type to operate with. + type VestingSchedule: VestingSchedule< + Self::AccountId, + Moment = Self::Moment, + Currency = Self::Currency, + >; + + /// The vesting schedule value itself. + type VestinScheduleValue: Get; + /// The getter for the current moment. type CurrentMoment: CurrentMoment; @@ -103,7 +111,7 @@ pub mod pallet { "Currencies must be init'd before vesting" ); - VestingSchedule::validate(::VestingSchedule, locked, start); + VestingSchedule::validate(&T::VestinScheduleValue::get(), locked, start); let vesting_info = VestingInfo { locked, start }; diff --git a/crates/vesting-schedule/src/lib.rs b/crates/pallet-vesting/src/vesting_schedule.rs similarity index 76% rename from crates/vesting-schedule/src/lib.rs rename to crates/pallet-vesting/src/vesting_schedule.rs index 4865b5ead..62ff73d63 100644 --- a/crates/vesting-schedule/src/lib.rs +++ b/crates/pallet-vesting/src/vesting_schedule.rs @@ -1,6 +1,4 @@ -//! The vesting schedule. - -#![cfg_attr(not(feature = "std"), no_std)] +//! Vesting schedule logic. use frame_support::traits::Currency as CurrencyT; use sp_arithmetic::traits::{ @@ -8,8 +6,34 @@ use sp_arithmetic::traits::{ UniqueSaturatedInto, Zero, }; -mod traits; -pub use traits::*; +/// [`VestingSchedule`] defines logic for currency vesting(unlocking). +pub trait VestingSchedule { + /// The type used to denote time: Timestamp, BlockNumber, etc. + type Moment; + /// The currency that this schedule applies to. + type Currency: CurrencyT; + /// An error that can occur at vesting schedule logic. + type Error; + /// Validate the schedule. + fn validate( + &self, + genesis_locked: >::Balance, + start: Self::Moment, + ) -> Result<(), Self::Error>; + /// Locked amount at provided moment. + fn locked_at( + &self, + genesis_locked: >::Balance, + start: Self::Moment, + moment: Self::Moment, + ) -> >::Balance; + /// Moment at which the schedule ends. + fn end( + &self, + genesis_locked: >::Balance, + start: Self::Moment, + ) -> Self::Moment; +} /// Implements linear vesting logic with cliff. pub struct LinearWithCliff> { diff --git a/crates/vesting-schedule/Cargo.toml b/crates/vesting-schedule/Cargo.toml deleted file mode 100644 index 0c3d98025..000000000 --- a/crates/vesting-schedule/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "vesting-schedule" -version = "0.1.0" -edition = "2021" -publish = false - -[dependencies] -frame-support = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "master" } -sp-arithmetic = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "master" } - -[features] -default = ["std"] -std = [ - "frame-support/std", - "sp-arithmetic/std", -] diff --git a/crates/vesting-schedule/src/traits.rs b/crates/vesting-schedule/src/traits.rs deleted file mode 100644 index d295d3898..000000000 --- a/crates/vesting-schedule/src/traits.rs +++ /dev/null @@ -1,32 +0,0 @@ -//! Generic vesting schedule related traits to abstract away the implementations. - -use frame_support::traits::Currency; - -/// [`VestingSchedule`] defines logic for currency vesting(unlocking). -pub trait VestingSchedule { - /// The type used to denote time: Timestamp, BlockNumber, etc. - type Moment; - /// The currency that this schedule applies to. - type Currency: Currency; - /// An error that can occur at vesting schedule logic. - type Error; - /// Validate the schedule. - fn validate( - &self, - genesis_locked: >::Balance, - start: Self::Moment, - ) -> Result<(), Self::Error>; - /// Locked amount at provided moment. - fn locked_at( - &self, - genesis_locked: >::Balance, - start: Self::Moment, - moment: Self::Moment, - ) -> >::Balance; - /// Moment at which the schedule ends. - fn end( - &self, - genesis_locked: >::Balance, - start: Self::Moment, - ) -> Self::Moment; -} From 39d5e4e1bc1fc771bcdbb8c7e2c9cfaa71492eda Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 28 Jul 2022 13:42:20 +0300 Subject: [PATCH 18/24] Delete vesting-logic --- Cargo.lock | 16 ----------- crates/vesting-logic/Cargo.toml | 17 ----------- crates/vesting-logic/src/lib.rs | 6 ---- crates/vesting-logic/src/traits.rs | 46 ------------------------------ 4 files changed, 85 deletions(-) delete mode 100644 crates/vesting-logic/Cargo.toml delete mode 100644 crates/vesting-logic/src/lib.rs delete mode 100644 crates/vesting-logic/src/traits.rs diff --git a/Cargo.lock b/Cargo.lock index da9ad5d01..152b55bc3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10178,22 +10178,6 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "vesting-logic" -version = "0.1.0" -dependencies = [ - "frame-support", - "vesting-schedule", -] - -[[package]] -name = "vesting-schedule" -version = "0.1.0" -dependencies = [ - "frame-support", - "sp-arithmetic", -] - [[package]] name = "void" version = "1.0.2" diff --git a/crates/vesting-logic/Cargo.toml b/crates/vesting-logic/Cargo.toml deleted file mode 100644 index fc539ddf3..000000000 --- a/crates/vesting-logic/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "vesting-logic" -version = "0.1.0" -edition = "2021" -publish = false - -[dependencies] -vesting-schedule = { version = "0.1", path = "../vesting-schedule", default-features = false } - -frame-support = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "master" } - -[features] -default = ["std"] -std = [ - "frame-support/std", - "vesting-schedule/std", -] diff --git a/crates/vesting-logic/src/lib.rs b/crates/vesting-logic/src/lib.rs deleted file mode 100644 index dace4bba3..000000000 --- a/crates/vesting-logic/src/lib.rs +++ /dev/null @@ -1,6 +0,0 @@ -//! The vesting. - -#![cfg_attr(not(feature = "std"), no_std)] - -mod traits; -pub use traits::*; diff --git a/crates/vesting-logic/src/traits.rs b/crates/vesting-logic/src/traits.rs deleted file mode 100644 index 92af92942..000000000 --- a/crates/vesting-logic/src/traits.rs +++ /dev/null @@ -1,46 +0,0 @@ -//! Generic vesting related traits to abstract away the implementations. - -use frame_support::{dispatch::DispatchResult, traits::Currency}; -use vesting_schedule::VestingSchedule; - -/// A general vesting logic. -pub trait Vesting { - /// Defines logic of vesting schedule to be used. - type VestingSchedule: VestingSchedule; - - /// Get the amount that is currently being vested and cannot be transferred out of this account. - /// Returns `None` if the account has no vesting schedule. - fn vesting_balance( - who: &AccountId, - ) -> Option< - <>::Currency as Currency>::Balance, - >; - - /// Adds a vesting schedule to a given account. - /// - /// If the account has `MaxVestingSchedules`, an Error is returned and nothing - /// is updated. - /// - /// Is a no-op if the amount to be vested is zero. - fn add_vesting_schedule( - who: &AccountId, - locked: <>::Currency as Currency< - AccountId, - >>::Balance, - start: >::Moment, - vesting_schedule: Self::VestingSchedule, - ) -> DispatchResult; - - /// Checks if `add_vesting_schedule` would work against `who`. - fn can_add_vesting_schedule( - who: &AccountId, - locked: <>::Currency as Currency< - AccountId, - >>::Balance, - start: >::Moment, - vesting_schedule: Self::VestingSchedule, - ) -> DispatchResult; - - /// Remove a vesting schedule for a given account. - fn remove_vesting_schedule(who: &AccountId, schedule_index: u32) -> DispatchResult; -} From 5b3f21f843e7f71c0c1a3e27f8a21737d2839ed7 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 28 Jul 2022 17:10:45 +0300 Subject: [PATCH 19/24] Introduce moment logic --- crates/pallet-vesting/src/moment.rs | 33 +++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 crates/pallet-vesting/src/moment.rs diff --git a/crates/pallet-vesting/src/moment.rs b/crates/pallet-vesting/src/moment.rs new file mode 100644 index 000000000..ec13e6bbf --- /dev/null +++ b/crates/pallet-vesting/src/moment.rs @@ -0,0 +1,33 @@ +//! The current moment logic. + +use sp_std::marker::PhantomData; + +pub trait CurrentMoment { + fn now() -> Moment; +} + +pub type UnixMilliseconds = u64; + +pub struct TimestampMoment(PhantomData); + +impl CurrentMoment for TimestampMoment +where + R: pallet_timestamp::Config, +{ + fn now() -> UnixMilliseconds { + pallet_timestamp::Pallet::::now() + } +} + +pub type BlockNumber = u32; + +pub struct BlockNumberMoment(PhantomData); + +impl CurrentMoment for BlockNumberMoment +where + R: frame_system::Config, +{ + fn now() -> BlockNumber { + frame_system::Pallet::::block_number() + } +} From 1c5a75ab9e17b5b207bf19532b0c7e44e0c3de58 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 28 Jul 2022 17:11:14 +0300 Subject: [PATCH 20/24] Define vesting driver logic --- crates/pallet-vesting/src/vesting_driver.rs | 27 +++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 crates/pallet-vesting/src/vesting_driver.rs diff --git a/crates/pallet-vesting/src/vesting_driver.rs b/crates/pallet-vesting/src/vesting_driver.rs new file mode 100644 index 000000000..fc9e1f515 --- /dev/null +++ b/crates/pallet-vesting/src/vesting_driver.rs @@ -0,0 +1,27 @@ +//! Vesting driver logic. + +use frame_support::traits::Currency as CurrencyT; + +use super::*; +use crate::{moment::CurrentMoment, vesting_schedule::VestingSchedule as VestingScheduleT}; + +/// [`VestingDriver`] logic. +pub trait VestingDriver> { + type CurrentMoment: CurrentMoment<>::Moment>; + /// Get the amount that is currently being vested and cannot be transferred out of this account. + fn vesting( + who: &AccountId, + vesting_info: &VestingInfo, + ) -> <>::Currency as CurrencyT>::Balance; +} + +pub struct VestingInfo, MaxSchedules> { + /// Locked amount at genesis. + pub locked: <>::Currency as CurrencyT< + AccountId, + >>::Balance, + /// Starting moment for unlocking(vesting). + pub start: >::Moment, + /// Vesting schedules. + pub schedules: BoundedVec, +} From de85df1f3b7c1c826f71fb1907e36b6b250cca6b Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 28 Jul 2022 17:12:49 +0300 Subject: [PATCH 21/24] Implement vesting-driver for timestamp --- .../src/vesting_driver_timestamp.rs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 crates/pallet-vesting/src/vesting_driver_timestamp.rs diff --git a/crates/pallet-vesting/src/vesting_driver_timestamp.rs b/crates/pallet-vesting/src/vesting_driver_timestamp.rs new file mode 100644 index 000000000..aba8689a3 --- /dev/null +++ b/crates/pallet-vesting/src/vesting_driver_timestamp.rs @@ -0,0 +1,37 @@ +use frame_support::traits::Currency as CurrencyT; +use sp_std::marker::PhantomData; + +use super::*; +use crate::{ + moment::{CurrentMoment, TimestampMoment, UnixMilliseconds}, + vesting_driver::{VestingDriver, VestingInfo}, + vesting_schedule::VestingSchedule as VestingScheduleT, +}; + +pub struct Driver(PhantomData); + +impl + VestingDriver, VestingSchedule> + for Driver +where + R: pallet_timestamp::Config, + VestingSchedule: VestingScheduleT, +{ + type CurrentMoment = TimestampMoment; + + fn vesting( + who: &AccountId, + vesting_info: &VestingInfo, + ) -> <>::Currency as CurrencyT>::Balance + { + let now = TimestampMoment::::now(); + let total_locked_now = vesting_info + .schedules + .iter() + .fold(Zero::zero(), |total, schedule| { + schedule.locked_at(vesting_info.locked, vesting_info.start, now) + }); + >::Currency::free_balance(who) + .min(total_locked_now) + } +} From f338475e6ef19bc13242b0b2e36671e40a8940ce Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 28 Jul 2022 17:13:41 +0300 Subject: [PATCH 22/24] Remove unused mod --- crates/pallet-vesting/src/vesting_info.rs | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 crates/pallet-vesting/src/vesting_info.rs diff --git a/crates/pallet-vesting/src/vesting_info.rs b/crates/pallet-vesting/src/vesting_info.rs deleted file mode 100644 index da23c877b..000000000 --- a/crates/pallet-vesting/src/vesting_info.rs +++ /dev/null @@ -1,12 +0,0 @@ -//! VestingInfo. - -use super::*; - -/// Struct to encode the vesting schedule of an individual account. -#[derive(Encode, Decode, Copy, Clone, PartialEq, Eq, RuntimeDebug, MaxEncodedLen, TypeInfo)] -pub struct VestingInfo { - /// Locked amount at genesis. - pub locked: Balance, - /// Starting moment for unlocking(vesting). - pub start: Moment, -} From 308c841c166c06bcc63d71662c4d3a8cee4eb7cc Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 28 Jul 2022 17:16:01 +0300 Subject: [PATCH 23/24] Initial pallet --- Cargo.lock | 1 + crates/pallet-vesting/Cargo.toml | 2 ++ crates/pallet-vesting/src/lib.rs | 58 ++------------------------------ 3 files changed, 6 insertions(+), 55 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 152b55bc3..e281c7a1e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5696,6 +5696,7 @@ version = "0.1.0" dependencies = [ "frame-support", "frame-system", + "pallet-timestamp", "parity-scale-codec", "scale-info", "serde", diff --git a/crates/pallet-vesting/Cargo.toml b/crates/pallet-vesting/Cargo.toml index 530887d29..e98535cd8 100644 --- a/crates/pallet-vesting/Cargo.toml +++ b/crates/pallet-vesting/Cargo.toml @@ -8,6 +8,7 @@ publish = false codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } frame-support = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "master" } frame-system = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "master" } +pallet-timestamp = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "master" } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1", features = ["derive"], optional = true } sp-arithmetic = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "master" } @@ -20,6 +21,7 @@ std = [ "codec/std", "frame-support/std", "frame-system/std", + "pallet-timestamp/std", "scale-info/std", "serde", "sp-arithmetic/std", diff --git a/crates/pallet-vesting/src/lib.rs b/crates/pallet-vesting/src/lib.rs index 8970c9de4..e11d24746 100644 --- a/crates/pallet-vesting/src/lib.rs +++ b/crates/pallet-vesting/src/lib.rs @@ -2,7 +2,9 @@ #![cfg_attr(not(feature = "std"), no_std)] -mod vesting_info; +mod moment; +mod vesting_driver; +mod vesting_driver_timestamp; mod vesting_schedule; use codec::MaxEncodedLen; @@ -13,14 +15,11 @@ use frame_support::{ }; pub use pallet::*; use sp_runtime::traits::{AtLeast32BitUnsigned, MaybeSerializeDeserialize, Zero}; -use vesting_info::VestingInfo; use vesting_schedule::VestingSchedule; /// Balance type alias. type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; -/// Full VestingInfo type. -type FullVestingInfo = VestingInfo, ::Moment>; /// The current storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); @@ -62,9 +61,6 @@ pub mod pallet { /// The vesting schedule value itself. type VestinScheduleValue: Get; - /// The getter for the current moment. - type CurrentMoment: CurrentMoment; - /// Maximum number of vesting schedules an account may have at a given moment. type MaxVestingSchedules: Get; @@ -75,52 +71,4 @@ pub mod pallet { #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); - - /// Information regarding the vesting of a given account. - #[pallet::storage] - #[pallet::getter(fn vesting)] - pub type Vesting = StorageMap< - _, - Blake2_128Concat, - T::AccountId, - BoundedVec, T::MaxVestingSchedules>, - >; - - #[pallet::genesis_config] - pub struct GenesisConfig { - /// The list of vesting to use. - pub vesting: Vec<(T::AccountId, BalanceOf, T::Moment)>, - } - - #[cfg(feature = "std")] - impl Default for GenesisConfig { - fn default() -> Self { - GenesisConfig { - vesting: Default::default(), - } - } - } - - #[pallet::genesis_build] - impl GenesisBuild for GenesisConfig { - fn build(&self) { - for &(ref who, locked, start) in self.vesting.iter() { - let balance = T::Currency::free_balance(who); - assert!( - !balance.is_zero(), - "Currencies must be init'd before vesting" - ); - - VestingSchedule::validate(&T::VestinScheduleValue::get(), locked, start); - - let vesting_info = VestingInfo { locked, start }; - - Vesting::::try_append(who, vesting_info) - .expect("Too many vesting schedules at genesis."); - - let reasons = WithdrawReasons::TRANSFER | WithdrawReasons::RESERVE; - T::Currency::set_lock(T::VestingLockId::get(), who, locked, reasons); - } - } - } } From 6f4fbcac683497754308e85b6ea641d8080a50c5 Mon Sep 17 00:00:00 2001 From: Dmitry Lavrenov Date: Thu, 28 Jul 2022 17:17:19 +0300 Subject: [PATCH 24/24] Sum locked --- .../src/vesting_driver_timestamp.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/crates/pallet-vesting/src/vesting_driver_timestamp.rs b/crates/pallet-vesting/src/vesting_driver_timestamp.rs index aba8689a3..da58fde75 100644 --- a/crates/pallet-vesting/src/vesting_driver_timestamp.rs +++ b/crates/pallet-vesting/src/vesting_driver_timestamp.rs @@ -1,4 +1,5 @@ use frame_support::traits::Currency as CurrencyT; +use sp_runtime::traits::Saturating; use sp_std::marker::PhantomData; use super::*; @@ -25,12 +26,15 @@ where ) -> <>::Currency as CurrencyT>::Balance { let now = TimestampMoment::::now(); - let total_locked_now = vesting_info - .schedules - .iter() - .fold(Zero::zero(), |total, schedule| { - schedule.locked_at(vesting_info.locked, vesting_info.start, now) - }); + let total_locked_now = + vesting_info + .schedules + .iter() + .fold(Zero::zero(), |total, schedule| { + schedule + .locked_at(vesting_info.locked, vesting_info.start, now) + .saturating_add(total) + }); >::Currency::free_balance(who) .min(total_locked_now) }