diff --git a/.github/workflows/generate_calamari_weights_files.yml b/.github/workflows/generate_calamari_weights_files.yml index 44686f25d..b2f76ddaf 100644 --- a/.github/workflows/generate_calamari_weights_files.yml +++ b/.github/workflows/generate_calamari_weights_files.yml @@ -144,6 +144,14 @@ jobs: id: pallet_balances name: pallet_balances iterations: 20 + - + extrinsic: + id: '*' + name: calamari_vesting + pallet: + id: calamari_vesting + name: calamari_vesting + iterations: 20 - extrinsic: id: '*' diff --git a/CHANGELOG.md b/CHANGELOG.md index 986c926f9..a48690dfb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,19 @@ ## Pending +## v3.1.0 ### Breaking changes ### Features +- [\#221](https://github.com/Manta-Network/Manta/pull/221) Add calamari-vesting pallet. +- [\#263](https://github.com/Manta-Network/Manta/pull/263) Calamari/Manta docker image and integration tests. +- [\#265](https://github.com/Manta-Network/Manta/pull/265) Integrate pallet-tx-pause in Manta/Calamari giving SUDO the ability to rapidly halt further execution of any extrinsic in the runtime. ### Improvements +- Bump spec version to 3100 +- [\#260](https://github.com/Manta-Network/Manta/pull/260) Update weight for `pallet_democracy`/`pallet_collective`/`pallet_membership`/`pallet_scheduler`/`pallet_balances`/`calamari-vesting`. +- [\#270](https://github.com/Manta-Network/Manta/pull/270) Whitelist `frame_system` calls and integrate custom `multisig` weights in Manta/Calamari runtimes. +- [\#279](https://github.com/Manta-Network/Manta/pull/279) CI improvements and custom weights for `pallet_session`, `pallet_timestamp`, `frame_system`. ### Bug fixes diff --git a/Cargo.lock b/Cargo.lock index 385cf2c4c..79297f128 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -941,6 +941,7 @@ checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" name = "calamari-runtime" version = "3.1.0" dependencies = [ + "calamari-vesting", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", @@ -1003,6 +1004,24 @@ dependencies = [ "xcm-executor", ] +[[package]] +name = "calamari-vesting" +version = "3.1.0" +dependencies = [ + "chrono", + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-balances", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "camino" version = "1.0.5" diff --git a/node/Cargo.toml b/node/Cargo.toml index 6bd50bea3..a044476eb 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -82,8 +82,8 @@ polkadot-primitives = { git = 'https://github.com/paritytech/polkadot.git', bran polkadot-service = { git = 'https://github.com/paritytech/polkadot.git', branch = "release-v0.9.12" } # Self dependencies -calamari-runtime = { path = '../runtime/calamari'} -manta-runtime = { path = '../runtime/manta'} +calamari-runtime = { path = '../runtime/calamari' } +manta-runtime = { path = '../runtime/manta' } manta-primitives = { path = '../runtime/primitives' } [build-dependencies] @@ -91,6 +91,7 @@ substrate-build-script-utils = { git = 'https://github.com/paritytech/substrate. [features] runtime-benchmarks = [ + 'calamari-runtime/runtime-benchmarks', 'polkadot-service/runtime-benchmarks', 'manta-runtime/runtime-benchmarks', ] diff --git a/node/src/chain_specs/manta.rs b/node/src/chain_specs/manta.rs index 19b03d729..7e5cf8495 100644 --- a/node/src/chain_specs/manta.rs +++ b/node/src/chain_specs/manta.rs @@ -188,7 +188,5 @@ pub fn manta_testnet_config(id: ParaId) -> Result { } pub fn manta_config() -> Result { - MantaChainSpec::from_json_bytes( - &include_bytes!("../../../genesis/manta-genesis.json")[..], - ) + MantaChainSpec::from_json_bytes(&include_bytes!("../../../genesis/manta-genesis.json")[..]) } diff --git a/pallets/vesting/Cargo.toml b/pallets/vesting/Cargo.toml new file mode 100644 index 000000000..488b94354 --- /dev/null +++ b/pallets/vesting/Cargo.toml @@ -0,0 +1,44 @@ +[package] +authors = ['Manta Network'] +name = "calamari-vesting" +version = '3.1.0' +edition = "2018" +homepage = 'https://manta.network' +license = 'GPL-3.0' +repository = 'https://github.com/Manta-Network/Manta/' + +[dependencies] +codec = { package = "parity-scale-codec", version = "2.3.1", default-features = false, features = ["derive"] } +scale-info = { version = "1.0", default-features = false, features = ["derive"] } +frame-benchmarking = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.12", optional = true } +pallet-timestamp = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.12", optional = true } +pallet-balances = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.12", optional = true } +frame-support = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.12" } +frame-system = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.12" } +sp-runtime = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.12" } +sp-std = { git = 'https://github.com/paritytech/substrate.git', default-features = false, branch = "polkadot-v0.9.12" } + +[dev-dependencies] +chrono = "0.4" +pallet-balances = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.12" } +pallet-timestamp = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.12" } +sp-core = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.12" } +sp-io = { git = 'https://github.com/paritytech/substrate.git', branch = "polkadot-v0.9.12" } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "sp-runtime/std", + "sp-std/std", +] +runtime-benchmarks = [ + "frame-benchmarking", + 'frame-support/runtime-benchmarks', + 'frame-system/runtime-benchmarks', + "pallet-timestamp/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", +] diff --git a/pallets/vesting/src/benchmarking.rs b/pallets/vesting/src/benchmarking.rs new file mode 100644 index 000000000..d36a61ce1 --- /dev/null +++ b/pallets/vesting/src/benchmarking.rs @@ -0,0 +1,132 @@ +// Copyright 2020-2021 Manta Network. +// This file is part of Manta. +// +// Manta is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Manta is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Manta. If not, see . + +#![cfg(feature = "runtime-benchmarks")] + +extern crate alloc; + +use super::*; +#[allow(unused_imports)] +use crate::Pallet as CalamariVesting; +use core::{convert::TryInto, ops::Div, time::Duration}; +use frame_benchmarking::{account, benchmarks, impl_benchmark_test_suite, whitelisted_caller}; +use frame_support::assert_ok; +use frame_system::RawOrigin; +use sp_runtime::{traits::AtLeast32BitUnsigned, SaturatedConversion}; + +const SEED: u32 = 0; +// existential deposit multiplier +const ED_MULTIPLIER: u32 = 100; + +fn assert_has_event(generic_event: ::Event) { + frame_system::Pallet::::assert_has_event(generic_event.into()); +} + +fn init_setup< + T: Config + pallet_timestamp::Config + pallet_balances::Config, + I: 'static, +>( + caller: &T::AccountId, +) where + ::Balance: TryFrom, + T: pallet_balances::Config, +{ + let now = Duration::from_secs(1636329600 - 1) + .as_millis() + .saturated_into::() + + 1; + pallet_timestamp::Pallet::::set_timestamp(now); + + let existential_deposit = >::ExistentialDeposit::get(); + let amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); + let source_caller = T::Lookup::unlookup(caller.clone()); + let _ = pallet_balances::Pallet::::make_free_balance_be(&caller, amount); + + assert_ok!(pallet_balances::Pallet::::set_balance( + RawOrigin::Root.into(), + source_caller, + amount, + amount + )); + assert_eq!( + pallet_balances::Pallet::::free_balance(caller), + amount + ); +} + +benchmarks! { + where_clause { + where + T: Config + pallet_timestamp::Config + pallet_balances::Config, + BalanceOf: AtLeast32BitUnsigned, + ::Balance: From, + } + + update_vesting_schedule { + let new_schedule = BoundedVec::try_from( + crate::Pallet::::vesting_schedule() + .iter() + .map(|(_, s)| s + 1) + .collect::>(), + ) + .unwrap_or_default(); + }: _(RawOrigin::Root, new_schedule.clone()) + verify { + assert_has_event::(Event::VestingScheduleUpdated(new_schedule).into()); + } + + vest { + let caller: T::AccountId = whitelisted_caller(); + let recipient: T::AccountId = account("receiver", 0, SEED); + let source_recipient = T::Lookup::unlookup(recipient.clone()); + + init_setup::(&caller); + let existential_deposit = >::ExistentialDeposit::get(); + let unvested = existential_deposit.saturating_mul(ED_MULTIPLIER.div(10u32).into()).saturated_into::().try_into().ok().unwrap(); + assert_ok!(crate::Pallet::::vested_transfer(RawOrigin::Signed(caller.clone()).into(), source_recipient, unvested)); + assert!(crate::Pallet::::vesting_balance(&recipient).is_some()); + + let now = Duration::from_secs(1660694400) + .as_millis() + .saturated_into::() + + 1; + pallet_timestamp::Pallet::::set_timestamp(now); + }: _(RawOrigin::Signed(recipient.clone())) + verify { + assert_has_event::(Event::VestingCompleted(recipient).into()); + } + + vested_transfer { + let caller: T::AccountId = whitelisted_caller(); + init_setup::(&caller); + let existential_deposit = >::ExistentialDeposit::get(); + let unvested = existential_deposit.saturating_mul(ED_MULTIPLIER.div(10u32).into()).saturated_into::().try_into().ok().unwrap(); + let recipient: T::AccountId = account("receiver", 0, SEED); + let source_recipient = T::Lookup::unlookup(recipient.clone()); + }: _(RawOrigin::Signed(caller.clone()), source_recipient, unvested) + verify { + assert_eq!(crate::Pallet::::vesting_balance(&recipient), Some(unvested)); + assert_has_event::(Event::VestingUpdated(recipient, unvested).into()); + } +} + +impl_benchmark_test_suite!( + CalamariVesting, + crate::mock::ExtBuilder::default() + .existential_deposit(1) + .build(), + crate::mock::Test, +); diff --git a/pallets/vesting/src/lib.rs b/pallets/vesting/src/lib.rs new file mode 100644 index 000000000..717100ec3 --- /dev/null +++ b/pallets/vesting/src/lib.rs @@ -0,0 +1,313 @@ +// Copyright 2020-2021 Manta Network. +// This file is part of Manta. +// +// Manta is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Manta is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Manta. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; +pub mod weights; + +use crate::weights::WeightInfo; +use core::convert::TryFrom; +use frame_support::{ + ensure, + pallet_prelude::*, + traits::{ + Currency, ExistenceRequirement, Get, LockIdentifier, LockableCurrency, UnixTime, + WithdrawReasons, + }, +}; +use frame_system::{ensure_signed, pallet_prelude::*}; +pub use pallet::*; +use sp_runtime::{ + traits::{Saturating, StaticLookup, Zero}, + Percent, +}; + +type BalanceOf = + <::Currency as Currency<::AccountId>>::Balance; +pub type Schedule = u64; + +const VESTING_ID: LockIdentifier = *b"calamvst"; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + /// The overarching event type. + type Event: From> + IsType<::Event>; + + /// The currency trait. + type Currency: LockableCurrency; + + type Timestamp: UnixTime; + + /// The minimum amount transferred to call `vested_transfer`. + #[pallet::constant] + type MinVestedTransfer: Get>; + + /// The maximum length of schedule is allowed. + #[pallet::constant] + type MaxScheduleLength: Get; + + type WeightInfo: crate::weights::WeightInfo; + } + + /// Information regarding the vesting of a given account. + #[pallet::storage] + #[pallet::getter(fn vesting_balance)] + pub(super) type VestingBalances = + StorageMap<_, Blake2_128Concat, T::AccountId, BalanceOf>; + + /// Information regarding the vesting of a given account. + #[pallet::storage] + #[pallet::getter(fn vesting_schedule)] + pub(super) type VestingSchedule = StorageValue< + _, + // The schedule is UTC. + BoundedVec<(Percent, Schedule), T::MaxScheduleLength>, + ValueQuery, + DefaultVestingSchedule, + >; + + #[pallet::type_value] + pub(super) fn DefaultVestingSchedule( + ) -> BoundedVec<(Percent, Schedule), T::MaxScheduleLength> { + BoundedVec::try_from(sp_std::vec![ + // 1639094400 = 2021-12-10 00:00:00(UTC) + (Percent::from_percent(45), 1639094400u64), + // 1641340800 = 2022-01-05 00:00:00(UTC) + (Percent::from_percent(11), 1641340800u64), + // 1646179200 = 2022-03-02 00:00:00(UTC) + (Percent::from_percent(11), 1646179200u64), + // 1651017600 = 2022-04-27 00:00:00(UTC) + (Percent::from_percent(11), 1651017600u64), + // 1655856000 = 2022-06-22 00:00:00(UTC) + (Percent::from_percent(11), 1655856000u64), + // 1660694400 = 2022-08-17 00:00:00(UTC) + (Percent::from_percent(11), 1660694400u64), + ]) + .unwrap_or_default() + } + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(_); + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// The amount vested has been updated. This could indicate more funds are available. + /// The balance given is the amount which is left unvested (and thus locked). + /// \[account, unvested\] + VestingUpdated(T::AccountId, BalanceOf), + /// An \[account\] has become fully vested. No further vesting can happen. + VestingCompleted(T::AccountId), + /// Update a vesting schedule. + /// \[new_schedule\] + VestingScheduleUpdated(BoundedVec), + } + + /// Error for the vesting pallet. + #[pallet::error] + pub enum Error { + /// The account given is not vesting. + NotVesting, + /// An existing vesting schedule already exists for this account that cannot be clobbered. + ExistingVestingSchedule, + /// Amount being transferred is too low to create a vesting schedule. + AmountLow, + /// Not enough tokens for vesting. + BalanceLow, + /// Cannot input + InvalidSchedule, + /// The length of new schedule cannot be bigger/smaller than 6. + InvalidScheduleLength, + /// The new schedule should be sorted. + UnsortedSchedule, + /// The first round of vesting is not done yet. + ClaimTooEarly, + } + + #[pallet::call] + impl Pallet { + /// Update vesting schedule. + /// + /// - `new_schedule`: New schedule for vesting. + #[pallet::weight(T::WeightInfo::update_vesting_schedule())] + pub fn update_vesting_schedule( + origin: OriginFor, + new_schedule: BoundedVec, + ) -> DispatchResult { + ensure_root(origin)?; + + // We have only 6 rounds of schedule. + let old_schedule = VestingSchedule::::get(); + ensure!( + new_schedule.len() == old_schedule.len(), + Error::::InvalidScheduleLength + ); + + // Ensure the new schedule should be sorted. + ensure!( + new_schedule.as_slice().windows(2).all(|w| w[0] < w[1]), + Error::::UnsortedSchedule + ); + + let now = T::Timestamp::now().as_secs(); + for (n, o) in new_schedule.iter().zip(old_schedule.iter()) { + // n == o means we will partialy update vesting schedule. + // n > o means new schedule is future schedule. + // n < o && n > now, also fine. + if o.1 <= now { + // Check partialy updating vesting schedule. + // We don't change past schedule, just skip it. + ensure!(*n == o.1, Error::::InvalidSchedule); + } else { + // New schedule should future time. + ensure!(*n >= now, Error::::InvalidSchedule); + } + } + + VestingSchedule::::mutate(|schedule| { + for (schedule, newer_schedule) in + schedule.as_mut().iter_mut().zip(new_schedule.iter()) + { + schedule.1 = *newer_schedule; + } + }); + + Self::deposit_event(Event::VestingScheduleUpdated(new_schedule)); + Ok(()) + } + + /// Unlock vested balance according to the schedule. + /// + /// The dispatch origin for this call must be _Signed_ and the sender must have funds still + /// locked under this pallet. + /// + /// Emits either `VestingCompleted` or `VestingUpdated`. + #[pallet::weight(T::WeightInfo::vest())] + pub fn vest(origin: OriginFor) -> DispatchResult { + let who = ensure_signed(origin)?; + + let now = T::Timestamp::now().as_secs(); + // Ensure signer can claim once time is up to schedule. + ensure!( + Some(now) >= VestingSchedule::::get().first().map(|v| v.1), + Error::::ClaimTooEarly + ); + + Self::update_lock(&who) + } + + /// Create a vested transfer: send `target` balance with the vesting schedule. + /// + /// The dispatch origin for this call must be _Signed_. + /// + /// - `target`: The account receiving the vested funds. + /// - `locked_amount`: How much tokens will be transfered. + #[pallet::weight(T::WeightInfo::vested_transfer())] + pub fn vested_transfer( + origin: OriginFor, + target: ::Source, + #[pallet::compact] locked_amount: BalanceOf, + ) -> DispatchResult { + let transactor = ensure_signed(origin)?; + ensure!( + locked_amount >= T::MinVestedTransfer::get(), + Error::::AmountLow + ); + + ensure!( + T::Currency::free_balance(&transactor) >= locked_amount, + Error::::BalanceLow + ); + + let who = T::Lookup::lookup(target)?; + ensure!( + !VestingBalances::::contains_key(&who), + Error::::ExistingVestingSchedule + ); + + T::Currency::transfer( + &transactor, + &who, + locked_amount, + ExistenceRequirement::AllowDeath, + )?; + + Self::new_vesting_account(&who, locked_amount)?; + + Ok(()) + } + } +} + +impl Pallet { + /// (Re)set pallet's currency lock on `who`'s account in accordance with their + /// current unvested amount. + fn update_lock(who: &T::AccountId) -> DispatchResult { + let vesting = Self::vesting_balance(&who).ok_or(Error::::NotVesting)?; + let now = T::Timestamp::now().as_secs(); + + // compute the vested portion + let mut portion = Percent::default(); + for (percentage, timestamp) in VestingSchedule::::get() { + if now < timestamp { + break; + } else { + portion = portion.saturating_add(percentage); + } + } + + let unvested = (Percent::from_percent(100) - portion) * vesting; + + if unvested.is_zero() { + T::Currency::remove_lock(VESTING_ID, who); + VestingBalances::::remove(&who); + Self::deposit_event(Event::::VestingCompleted(who.clone())); + } else { + let reasons = WithdrawReasons::TRANSFER | WithdrawReasons::RESERVE; + T::Currency::set_lock(VESTING_ID, who, unvested, reasons); + Self::deposit_event(Event::::VestingUpdated(who.clone(), unvested)); + } + Ok(()) + } + + /// Adds a vesting schedule to a given account. + fn new_vesting_account(who: &T::AccountId, locked: BalanceOf) -> DispatchResult { + if locked.is_zero() { + return Ok(()); + } + + // Ensure current user doesn't have any vested token. + ensure!( + !VestingBalances::::contains_key(&who), + Error::::ExistingVestingSchedule + ); + + VestingBalances::::insert(&who, locked); + // it can't fail, but even if somehow it did, we don't really care. + Self::update_lock(who) + } +} diff --git a/pallets/vesting/src/mock.rs b/pallets/vesting/src/mock.rs new file mode 100644 index 000000000..aa1dd628c --- /dev/null +++ b/pallets/vesting/src/mock.rs @@ -0,0 +1,162 @@ +// Copyright 2020-2021 Manta Network. +// This file is part of Manta. +// +// Manta is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Manta is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Manta. If not, see . + +use frame_support::parameter_types; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, IdentityLookup}, +}; + +use super::*; +use crate as calamari_vesting; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +pub type AccountId = u128; +pub type BlockNumber = u64; +pub type Balance = u128; + +pub const ALICE: AccountId = 1; +pub const BOB: AccountId = 2; +pub const ALICE_DEPOSIT: Balance = 10_000; + +frame_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + CalamariVesting: calamari_vesting::{Pallet, Call, Storage, Event}, + } +); + +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub BlockWeights: frame_system::limits::BlockWeights = + frame_system::limits::BlockWeights::simple_max(1024); +} +impl frame_system::Config for Test { + type AccountData = pallet_balances::AccountData; + type AccountId = AccountId; + type BaseCallFilter = frame_support::traits::Everything; + type BlockHashCount = BlockHashCount; + type BlockLength = (); + type BlockNumber = BlockNumber; + type BlockWeights = (); + type Call = Call; + type DbWeight = (); + type Event = Event; + type Hash = H256; + type Hashing = BlakeTwo256; + type Header = Header; + type Index = u64; + type Lookup = IdentityLookup; + type OnKilledAccount = (); + type OnNewAccount = (); + type OnSetCode = (); + type Origin = Origin; + type PalletInfo = PalletInfo; + type SS58Prefix = (); + type SystemWeightInfo = (); + type Version = (); +} + +parameter_types! { + pub const MaxLocks: u32 = 10; +} +impl pallet_balances::Config for Test { + type AccountStore = System; + type Balance = Balance; + type DustRemoval = (); + type Event = Event; + type ExistentialDeposit = ExistentialDeposit; + type MaxLocks = MaxLocks; + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type WeightInfo = (); +} + +parameter_types! { + pub const MinimumPeriod: u64 = 12_000 / 2; +} +impl pallet_timestamp::Config for Test { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; + type WeightInfo = (); +} + +parameter_types! { + pub const MinVestedTransfer: Balance = 2; + pub static ExistentialDeposit: Balance = 1; + pub const MaxScheduleLength: u32 = 6; +} +impl Config for Test { + type Currency = Balances; + type Event = Event; + type Timestamp = Timestamp; + type MinVestedTransfer = MinVestedTransfer; + type MaxScheduleLength = MaxScheduleLength; + type WeightInfo = (); +} + +pub struct ExtBuilder { + existential_deposit: Balance, +} + +impl Default for ExtBuilder { + fn default() -> Self { + Self { + existential_deposit: 1, + } + } +} + +impl ExtBuilder { + pub fn existential_deposit(mut self, existential_deposit: Balance) -> Self { + self.existential_deposit = existential_deposit; + self + } + + pub fn build(self) -> sp_io::TestExternalities { + EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); + let mut t = frame_system::GenesisConfig::default() + .build_storage::() + .unwrap(); + pallet_balances::GenesisConfig:: { + balances: vec![(ALICE, ALICE_DEPOSIT * self.existential_deposit)], + } + .assimilate_storage(&mut t) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + // Set current time more early than the first schedule. + Timestamp::set_timestamp(VestingSchedule::::get()[0].1 * 1000 - 3 * 6000); + System::set_block_number(1); + }); + ext + } +} + +pub(crate) fn run_to_block(n: u64) { + System::set_block_number(n); +} diff --git a/pallets/vesting/src/tests.rs b/pallets/vesting/src/tests.rs new file mode 100644 index 000000000..fbc38c8c8 --- /dev/null +++ b/pallets/vesting/src/tests.rs @@ -0,0 +1,530 @@ +// Copyright 2020-2021 Manta Network. +// This file is part of Manta. +// +// Manta is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Manta is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Manta. If not, see . + +use super::{Event as PalletEvent, *}; +use chrono::prelude::*; +use frame_support::{assert_noop, assert_ok}; +use mock::{Event as MockEvent, *}; + +#[test] +fn alice_vesting_for_bob_should_work() { + ExtBuilder::default() + .existential_deposit(1) + .build() + .execute_with(|| { + // Cannot vest tokens that is less than expected. + assert_noop!( + CalamariVesting::vested_transfer( + Origin::signed(ALICE), + BOB, + MinVestedTransfer::get() - 1 + ), + Error::::AmountLow + ); + + // Signer cannot vest tokens that exceeds all he has. + assert_noop!( + CalamariVesting::vested_transfer(Origin::signed(ALICE), BOB, ALICE_DEPOSIT + 1), + Error::::BalanceLow + ); + + let unvested = 100; + assert_ok!(CalamariVesting::vested_transfer( + Origin::signed(ALICE), + BOB, + unvested + )); + + // Cannot vest tokens the same user more than twice. + assert_noop!( + CalamariVesting::vested_transfer(Origin::signed(ALICE), BOB, unvested), + Error::::ExistingVestingSchedule + ); + + assert_eq!(Balances::free_balance(ALICE), ALICE_DEPOSIT - unvested); + assert_eq!(Balances::free_balance(BOB), unvested); + assert_eq!(VestingBalances::::get(BOB), Some(unvested)); + + // Now Bob cannot claim any token. + assert_noop!( + CalamariVesting::vest(Origin::signed(BOB)), + Error::::ClaimTooEarly, + ); + + // Check event + System::assert_has_event(MockEvent::CalamariVesting(PalletEvent::VestingUpdated( + BOB, unvested, + ))); + + run_to_block(3); + // Ensure current timestamp is bigger than the 1th round of schedule. + // Now Bob can claim 1th round vested tokens. + let first_round = 0; + let now = VestingSchedule::::get()[first_round].1 * 1000 + 1; + Timestamp::set_timestamp(now); + + assert_ok!(CalamariVesting::vest(Origin::signed(BOB))); + assert_eq!(Balances::free_balance(BOB), unvested); + + // BOB cannot transfer more than 1th round of vested tokens. + // Bacause the rest of tokens are locked. + let vested = VestingSchedule::::get()[first_round].0 * unvested; + // Check event + System::assert_has_event(MockEvent::CalamariVesting(PalletEvent::VestingUpdated( + BOB, + unvested - vested, + ))); + + assert_noop!( + Balances::transfer(Origin::signed(BOB), ALICE, vested + 1), + pallet_balances::Error::::LiquidityRestrictions, + ); + + assert_ok!(Balances::transfer(Origin::signed(BOB), ALICE, vested)); + assert_eq!( + Balances::free_balance(ALICE), + ALICE_DEPOSIT - unvested + vested + ); + assert_eq!(Balances::free_balance(BOB), unvested - vested); + + // Ensure current timestamp is bigger than the 6th round of schedule. + // Now Bob can claim 6th round vested tokens. + let last_round = 5; + let now = VestingSchedule::::get()[last_round].1 * 1000 + 1; + Timestamp::set_timestamp(now); + + assert_ok!(CalamariVesting::vest(Origin::signed(BOB))); + assert_eq!(Balances::free_balance(BOB), unvested - vested); + + // Check vested done event + System::assert_has_event(MockEvent::CalamariVesting(PalletEvent::VestingCompleted( + BOB, + ))); + + // Now, Bob can transfer all his tokens. + assert_ok!(Balances::transfer( + Origin::signed(BOB), + ALICE, + unvested - vested + )); + assert_eq!(Balances::free_balance(ALICE), ALICE_DEPOSIT); + assert_eq!(Balances::free_balance(BOB), 0); + + // Ensure vesting info is removed once vesting is done. + assert_eq!(VestingBalances::::get(BOB), None); + }); +} + +#[test] +fn alice_vesting_for_bob_claim_slowly_should_work() { + ExtBuilder::default() + .existential_deposit(1) + .build() + .execute_with(|| { + let unvested = 100; + assert_ok!(CalamariVesting::vested_transfer( + Origin::signed(ALICE), + BOB, + unvested + )); + assert_eq!(Balances::free_balance(ALICE), ALICE_DEPOSIT - unvested); + assert_eq!(Balances::free_balance(BOB), unvested); + assert_eq!(VestingBalances::::get(BOB), Some(unvested)); + + // Now Bob cannot claim any token. + assert_noop!( + CalamariVesting::vest(Origin::signed(BOB)), + Error::::ClaimTooEarly, + ); + + // Check event + System::assert_has_event(MockEvent::CalamariVesting(PalletEvent::VestingUpdated( + BOB, unvested, + ))); + + // Ensure current timestamp is bigger than the 4th round of schedule. + // Now Bob can claim 4th round vested tokens. + let fourth_round = 3; + let now = VestingSchedule::::get()[fourth_round].1 * 1000 + 1; + Timestamp::set_timestamp(now); + + assert_ok!(CalamariVesting::vest(Origin::signed(BOB))); + assert_eq!(Balances::free_balance(BOB), unvested); + + // Calculate how many tokens that have been vested. + let vested = VestingSchedule::::get()[..=fourth_round] + .iter() + .map(|s| s.0) + .fold(Percent::from_percent(0), |acc, p| acc.saturating_add(p)) + * unvested; + assert_noop!( + Balances::transfer(Origin::signed(BOB), ALICE, vested + 1), + pallet_balances::Error::::LiquidityRestrictions, + ); + + assert_ok!(Balances::transfer(Origin::signed(BOB), ALICE, vested)); + assert_eq!( + Balances::free_balance(ALICE), + ALICE_DEPOSIT - unvested + vested + ); + assert_eq!(Balances::free_balance(BOB), unvested - vested); + }); +} + +#[test] +fn alice_vesting_for_bob_claim_arbitrarily_should_work() { + ExtBuilder::default() + .existential_deposit(1) + .build() + .execute_with(|| { + let unvested = 100; + assert_ok!(CalamariVesting::vested_transfer( + Origin::signed(ALICE), + BOB, + unvested + )); + assert_eq!(Balances::free_balance(ALICE), ALICE_DEPOSIT - unvested); + assert_eq!(Balances::free_balance(BOB), unvested); + assert_eq!(VestingBalances::::get(BOB), Some(unvested)); + + run_to_block(3); + // Ensure current timestamp is bigger than the 1th round of schedule. + // Now Bob can claim 1th round vested tokens. + let first_round = 0; + let now = VestingSchedule::::get()[first_round].1 * 1000 + 1; + Timestamp::set_timestamp(now); + + assert_ok!(CalamariVesting::vest(Origin::signed(BOB))); + assert_eq!(Balances::free_balance(BOB), unvested); + + // BOB cannot transfer more than 1th round of vested tokens. + // Bacause the rest of tokens are locked. + let vested_1 = VestingSchedule::::get()[first_round].0 * unvested; + // Check event + System::assert_has_event(MockEvent::CalamariVesting(PalletEvent::VestingUpdated( + BOB, + unvested - vested_1, + ))); + + assert_noop!( + Balances::transfer(Origin::signed(BOB), ALICE, vested_1 + 1), + pallet_balances::Error::::LiquidityRestrictions, + ); + + assert_ok!(Balances::transfer(Origin::signed(BOB), ALICE, vested_1)); + assert_eq!( + Balances::free_balance(ALICE), + ALICE_DEPOSIT - unvested + vested_1 + ); + assert_eq!(Balances::free_balance(BOB), unvested - vested_1); + + // Ensure current timestamp is bigger than the 5th round of schedule. + // Now Bob can claim 5th round vested tokens. + let sixth_round = 4; + let now = VestingSchedule::::get()[sixth_round].1 * 1000 + 1; + Timestamp::set_timestamp(now); + + assert_ok!(CalamariVesting::vest(Origin::signed(BOB))); + + // All vested for 5th round. + let vested_0_to_4 = VestingSchedule::::get()[..=sixth_round] + .iter() + .map(|s| s.0) + .fold(Percent::from_percent(0), |acc, p| acc.saturating_add(p)) + * unvested; + assert_noop!( + Balances::transfer(Origin::signed(BOB), ALICE, vested_0_to_4 + 1 - vested_1), + pallet_balances::Error::::LiquidityRestrictions, + ); + + // Vested only 6th round. + let vested_5 = VestingSchedule::::get()[sixth_round].0 * unvested; + + // Check event + System::assert_has_event(MockEvent::CalamariVesting(PalletEvent::VestingUpdated( + BOB, 11, + ))); + assert_eq!( + Balances::free_balance(BOB), + vested_0_to_4 + vested_5 - vested_1 + ); + + assert_ok!(Balances::transfer( + Origin::signed(BOB), + ALICE, + vested_0_to_4 - vested_1 + )); + assert_eq!(Balances::free_balance(ALICE), ALICE_DEPOSIT - vested_5); + assert_eq!(Balances::free_balance(BOB), vested_5); + }); +} + +#[test] +fn vesting_complete_should_work() { + ExtBuilder::default() + .existential_deposit(1) + .build() + .execute_with(|| { + let unvested = 100; + assert_ok!(CalamariVesting::vested_transfer( + Origin::signed(ALICE), + BOB, + unvested + )); + assert_eq!(Balances::free_balance(ALICE), ALICE_DEPOSIT - unvested); + assert_eq!(VestingBalances::::get(BOB), Some(unvested)); + + // Now Bob cannot claim any token. + assert_noop!( + CalamariVesting::vest(Origin::signed(BOB)), + Error::::ClaimTooEarly, + ); + + // Check event + System::assert_has_event(MockEvent::CalamariVesting(PalletEvent::VestingUpdated( + BOB, unvested, + ))); + + // Now Bob cannot transfer locked tokens. + assert_noop!( + Balances::transfer(Origin::signed(BOB), ALICE, 1), + pallet_balances::Error::::LiquidityRestrictions, + ); + + // Ensure current timestamp is bigger than the 6th round of schedule. + // Now Bob can claim 6th round vested tokens. + let last_round = 5; + let now = VestingSchedule::::get()[last_round].1 * 1000 + 1; + Timestamp::set_timestamp(now); + + assert_ok!(CalamariVesting::vest(Origin::signed(BOB))); + assert_eq!(Balances::free_balance(BOB), unvested); + + // Check vested done event + System::assert_has_event(MockEvent::CalamariVesting(PalletEvent::VestingCompleted( + BOB, + ))); + let vested = unvested; + + // Now, Bob can transfer all his tokens. + assert_ok!(Balances::transfer(Origin::signed(BOB), ALICE, vested)); + assert_eq!(Balances::free_balance(ALICE), ALICE_DEPOSIT); + assert_eq!(Balances::free_balance(BOB), 0); + + // Ensure vesting info is removed once vesting is done. + assert_eq!(VestingBalances::::get(BOB), None); + }); +} + +#[test] +fn partially_update_vesting_schedule_should_work() { + ExtBuilder::default() + .existential_deposit(1) + .build() + .execute_with(|| { + // Ensure current timestamp is bigger than the 1th round of schedule. + // Now Bob can claim 1th round vested tokens. + let frist_round = 0; + let now = VestingSchedule::::get()[frist_round].1 * 1000 + 1; + Timestamp::set_timestamp(now); + + // skip 2 round of old schedule. + let skipped_count = 2; + let new_schedule = BoundedVec::try_from({ + let mut new_schedule = vec![]; + for (index, (_, schedule)) in VestingSchedule::::get().iter().enumerate() { + if index < skipped_count { + // Do not change old schedule + new_schedule.push(*schedule); + continue; + } + // odd means more early than old schedle but still later than now. + // even means more late than old schedle but still later than now. + if index % 2 == 0 { + new_schedule.push(*schedule + 1); + } else { + new_schedule.push(*schedule - 1); + } + } + new_schedule + }) + .unwrap_or_default(); + + assert_ok!(CalamariVesting::update_vesting_schedule( + Origin::root(), + new_schedule.clone() + )); + // Check storage + assert_eq!( + VestingSchedule::::get() + .iter() + .map(|(_, s)| *s) + .collect::>(), + *new_schedule + ); + // Check event + System::assert_has_event(MockEvent::CalamariVesting( + PalletEvent::VestingScheduleUpdated(new_schedule), + )); + }); +} + +#[test] +fn update_brand_new_vesting_schedule_should_work() { + ExtBuilder::default() + .existential_deposit(1) + .build() + .execute_with(|| { + // Ensure current timestamp is bigger than the 1th round of schedule. + // Now Bob can claim 1th round vested tokens. + let frist_round = 0; + let now = VestingSchedule::::get()[frist_round].1 * 1000 - 1; + Timestamp::set_timestamp(now); + + let new_schedule = BoundedVec::try_from( + VestingSchedule::::get() + .iter() + .map(|(_, s)| s + 1) + .collect::>(), + ) + .unwrap_or_default(); + assert_ok!(CalamariVesting::update_vesting_schedule( + Origin::root(), + new_schedule.clone() + )); + // Check storage + assert_eq!( + VestingSchedule::::get() + .iter() + .map(|(_, s)| *s) + .collect::>(), + *new_schedule + ); + // Check event + System::assert_has_event(MockEvent::CalamariVesting( + PalletEvent::VestingScheduleUpdated(new_schedule), + )); + }); +} + +#[test] +fn invalid_schedule_should_not_be_updated() { + ExtBuilder::default() + .existential_deposit(1) + .build() + .execute_with(|| { + // Cannot update the length of schedule is bigger than 6 or smaller than 6. + let wrong_length_schedule: BoundedVec = + BoundedVec::try_from(vec![1, 2, 3, 4, 5, 6, 7]).unwrap_or_default(); + assert_noop!( + CalamariVesting::update_vesting_schedule(Origin::root(), wrong_length_schedule), + Error::::InvalidScheduleLength, + ); + + // We have only 6 rounds of schedule. + let wrong_length_schedule: BoundedVec = + BoundedVec::try_from(vec![1, 2, 3, 4, 5]).unwrap_or_default(); + assert_noop!( + CalamariVesting::update_vesting_schedule(Origin::root(), wrong_length_schedule), + Error::::InvalidScheduleLength, + ); + + // The new schedule should be a sorted array. + let invalid_schedule: BoundedVec = + BoundedVec::try_from(vec![1, 2, 9, 4, 8, 6]).unwrap_or_default(); + assert_noop!( + CalamariVesting::update_vesting_schedule(Origin::root(), invalid_schedule), + Error::::UnsortedSchedule, + ); + + // Check updating invalid partial schedule should not work. + let next_round = 3; + // now is between 3th round and 4th round. + let now = (VestingSchedule::::get()[next_round].1 - 1) * 1000; + Timestamp::set_timestamp(now); + + let invalid_schedule = BoundedVec::try_from({ + let mut new_schedule = vec![]; + for (index, (_, schedule)) in VestingSchedule::::get().iter().enumerate() { + if index < next_round { + // Do not change old schedule + new_schedule.push(*schedule); + continue; + } + // Set one schedule that is past time. + // This schedule is earlier than now. + if index == next_round { + new_schedule.push((now - 2) / 1000); + continue; + } + // Do not change the rest of future schedule; + new_schedule.push(*schedule); + } + new_schedule + }) + .unwrap_or_default(); + + assert_noop!( + CalamariVesting::update_vesting_schedule(Origin::root(), invalid_schedule), + Error::::InvalidSchedule, + ); + }); +} + +#[test] +fn check_vesting_schedule() { + #[rustfmt::skip] + let default_schedule: [(Percent, (i32, u32, u32, u32, u32, u32), &'static str); 6] = [ + // (Percentage, (timestamp), date) + (Percent::from_percent(45), (2021, 12, 10, 0, 0, 0), "2021-12-10 00:00:00"), + (Percent::from_percent(11), (2022, 01, 05, 0, 0, 0), "2022-01-05 00:00:00"), + (Percent::from_percent(11), (2022, 03, 02, 0, 0, 0), "2022-03-02 00:00:00"), + (Percent::from_percent(11), (2022, 04, 27, 0, 0, 0), "2022-04-27 00:00:00"), + (Percent::from_percent(11), (2022, 06, 22, 0, 0, 0), "2022-06-22 00:00:00"), + (Percent::from_percent(11), (2022, 08, 17, 0, 0, 0), "2022-08-17 00:00:00"), + ]; + + ExtBuilder::default() + .existential_deposit(1) + .build() + .execute_with(|| { + // Check current schedule. + let schedule = VestingSchedule::::get(); + assert_eq!(schedule.len(), MaxScheduleLength::get() as usize); + + //Check percentage. + assert_eq!( + schedule + .iter() + .map(|(p, _)| p) + .fold(Percent::from_percent(0), |acc, p| acc.saturating_add(*p)), + Percent::from_percent(100) + ); + + for ((p, s), ds) in schedule.iter().zip(default_schedule.iter()) { + let dt = Utc + .ymd(ds.1 .0, ds.1 .1, ds.1 .2) + .and_hms(ds.1 .3, ds.1 .4, ds.1 .5); + + // Check each percentage is correct. + assert_eq!(ds.0, *p); + // Check datetime is correct. + assert_eq!(dt.format("%Y-%m-%d %H:%M:%S").to_string(), ds.2); + // Check timestamp is correct. + assert_eq!(dt.timestamp() as u64, *s); + } + }); +} diff --git a/pallets/vesting/src/weights.rs b/pallets/vesting/src/weights.rs new file mode 100644 index 000000000..a0d13393e --- /dev/null +++ b/pallets/vesting/src/weights.rs @@ -0,0 +1,113 @@ +// Copyright 2020-2021 Manta Network. +// This file is part of Manta. + +// Manta is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Manta is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Manta. If not, see . + +//! Autogenerated weights for calamari_vesting +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2021-11-22, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("calamari-dev"), DB CACHE: 128 + +// Executed Command: +// manta +// benchmark +// --chain=calamari-dev +// --pallet=calamari_vesting +// --extrinsic=* +// --execution=Wasm +// --wasm-execution=Compiled +// --heap-pages=4096 +// --repeat=20 +// --steps=50 +// --template=.github/resources/frame-weight-template.hbs +// --output=calamari_vesting.rs + + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for calamari_vesting. +pub trait WeightInfo { + fn update_vesting_schedule() -> Weight; + fn vest() -> Weight; + fn vested_transfer() -> Weight; +} + +/// Weights for calamari_vesting using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + // Storage: CalamariVesting VestingSchedule (r:1 w:1) + // Storage: Timestamp Now (r:1 w:0) + fn update_vesting_schedule() -> Weight { + (47_829_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + // Storage: Timestamp Now (r:1 w:0) + // Storage: CalamariVesting VestingSchedule (r:1 w:0) + // Storage: CalamariVesting VestingBalances (r:1 w:1) + // Storage: Balances Locks (r:1 w:1) + // Storage: System Account (r:1 w:1) + fn vest() -> Weight { + (110_048_000 as Weight) + .saturating_add(T::DbWeight::get().reads(5 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + } + // Storage: CalamariVesting VestingBalances (r:1 w:1) + // Storage: System Account (r:1 w:1) + // Storage: Timestamp Now (r:1 w:0) + // Storage: CalamariVesting VestingSchedule (r:1 w:0) + // Storage: Balances Locks (r:1 w:1) + fn vested_transfer() -> Weight { + (216_897_000 as Weight) + .saturating_add(T::DbWeight::get().reads(5 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + // Storage: CalamariVesting VestingSchedule (r:1 w:1) + // Storage: Timestamp Now (r:1 w:0) + fn update_vesting_schedule() -> Weight { + (47_829_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + } + // Storage: Timestamp Now (r:1 w:0) + // Storage: CalamariVesting VestingSchedule (r:1 w:0) + // Storage: CalamariVesting VestingBalances (r:1 w:1) + // Storage: Balances Locks (r:1 w:1) + // Storage: System Account (r:1 w:1) + fn vest() -> Weight { + (110_048_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(5 as Weight)) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + } + // Storage: CalamariVesting VestingBalances (r:1 w:1) + // Storage: System Account (r:1 w:1) + // Storage: Timestamp Now (r:1 w:0) + // Storage: CalamariVesting VestingSchedule (r:1 w:0) + // Storage: Balances Locks (r:1 w:1) + fn vested_transfer() -> Weight { + (216_897_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(5 as Weight)) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + } +} diff --git a/runtime/calamari/Cargo.toml b/runtime/calamari/Cargo.toml index 2dffccfec..49a063f3e 100644 --- a/runtime/calamari/Cargo.toml +++ b/runtime/calamari/Cargo.toml @@ -78,6 +78,7 @@ pallet-xcm = { git = 'https://github.com/paritytech/polkadot.git', default-featu # Self dependencies manta-primitives = { path = '../primitives', default-features = false } +calamari-vesting = { path = '../../pallets/vesting', default-features = false } pallet-tx-pause = { path = '../../pallets/pallet-tx-pause', default-features = false } [package.metadata.docs.rs] @@ -92,7 +93,6 @@ try-runtime = [ 'frame-executive/try-runtime', 'frame-try-runtime', ] - runtime-benchmarks = [ 'cumulus-pallet-session-benchmarking/runtime-benchmarks', 'hex-literal', @@ -112,6 +112,7 @@ runtime-benchmarks = [ 'pallet-democracy/runtime-benchmarks', 'pallet-scheduler/runtime-benchmarks', 'pallet-membership/runtime-benchmarks', + 'calamari-vesting/runtime-benchmarks', 'pallet-tx-pause/runtime-benchmarks', ] std = [ @@ -165,5 +166,6 @@ std = [ 'polkadot-runtime-common/std', 'polkadot-primitives/std', 'pallet-collator-selection/std', + 'calamari-vesting/std', 'pallet-tx-pause/std', ] diff --git a/runtime/calamari/src/lib.rs b/runtime/calamari/src/lib.rs index 0486b953c..e4f1f3158 100644 --- a/runtime/calamari/src/lib.rs +++ b/runtime/calamari/src/lib.rs @@ -107,7 +107,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("calamari"), impl_name: create_runtime_str!("calamari"), authoring_version: 1, - spec_version: 3090, + spec_version: 3100, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 2, @@ -220,6 +220,7 @@ impl Contains for BaseFilter { | Call::CouncilMembership(_) | Call::TechnicalMembership(_) | Call::Scheduler(_) + | Call::CalamariVesting(_) | Call::Balances(_) => true, _ => false, // Filter Utility to prevent users from setting keys and selecting collator for parachain (couldn't use now). @@ -726,6 +727,21 @@ impl pallet_collator_selection::Config for Runtime { type WeightInfo = pallet_collator_selection::weights::SubstrateWeight; } +// Calamari pallets configuration +parameter_types! { + pub const MinVestedTransfer: Balance = KMA; + pub const MaxScheduleLength: u32 = 6; +} + +impl calamari_vesting::Config for Runtime { + type Currency = Balances; + type Event = Event; + type Timestamp = Timestamp; + type MinVestedTransfer = MinVestedTransfer; + type MaxScheduleLength = MaxScheduleLength; + type WeightInfo = calamari_vesting::weights::SubstrateWeight; +} + // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub enum Runtime where @@ -773,6 +789,9 @@ construct_runtime!( Utility: pallet_utility::{Pallet, Call, Event} = 40, Multisig: pallet_multisig::{Pallet, Call, Storage, Event} = 41, Sudo: pallet_sudo::{Pallet, Call, Config, Storage, Event} = 42, + + // Calamari stuff + CalamariVesting: calamari_vesting::{Pallet, Call, Storage, Event} = 50, } ); @@ -949,6 +968,7 @@ impl_runtime_apis! { list_benchmark!(list, extra, pallet_collective, Council); list_benchmark!(list, extra, pallet_membership, CouncilMembership); list_benchmark!(list, extra, pallet_scheduler, Scheduler); + list_benchmark!(list, extra, calamari_vesting, CalamariVesting); list_benchmark!(list, extra, pallet_session, SessionBench::); list_benchmark!(list, extra, pallet_tx_pause, TransactionPause); @@ -995,6 +1015,7 @@ impl_runtime_apis! { add_benchmark!(params, batches, pallet_collective, Council); add_benchmark!(params, batches, pallet_membership, CouncilMembership); add_benchmark!(params, batches, pallet_scheduler, Scheduler); + add_benchmark!(params, batches, calamari_vesting, CalamariVesting); add_benchmark!(params, batches, pallet_session, SessionBench::); add_benchmark!(params, batches, pallet_tx_pause, TransactionPause); diff --git a/runtime/manta/src/lib.rs b/runtime/manta/src/lib.rs index 035f5face..6fc977d44 100644 --- a/runtime/manta/src/lib.rs +++ b/runtime/manta/src/lib.rs @@ -106,7 +106,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("manta"), impl_name: create_runtime_str!("manta"), authoring_version: 1, - spec_version: 3090, + spec_version: 3100, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, diff --git a/runtime/primitives/Cargo.toml b/runtime/primitives/Cargo.toml index 2a86d7297..36c6eb894 100644 --- a/runtime/primitives/Cargo.toml +++ b/runtime/primitives/Cargo.toml @@ -1,7 +1,7 @@ [package] authors = ['Manta Network'] name = "manta-primitives" -version = "3.1.0" +version = '3.1.0' edition = "2018" homepage = 'https://manta.network' license = 'GPL-3.0'