diff --git a/README.md b/README.md index 0529d2f2f..78a1140b4 100644 --- a/README.md +++ b/README.md @@ -1 +1,7 @@ -# gamedao-protocol +# GAMEDAO PROTOCOL + +GameDAO Protocol is a toolbox to enable setup and operation of unstoppable on chain collectives. It offers governance via proposals for members of a collective, helping to notarize and enforce decisions, e.g. treasury spending. Collectives can also fundraise through the protocol, enabling them to kickstart their efforts. + +### Copyright + +(c) GameDAO AG diff --git a/battlepass/Cargo.toml b/battlepass/Cargo.toml new file mode 100644 index 000000000..4d813d930 --- /dev/null +++ b/battlepass/Cargo.toml @@ -0,0 +1,69 @@ +[package] +name = "gamedao-battlepass" +version = "1.3.0" +authors = ["zero.io","gamedao.co"] +repository = "https://github.com/gamedaoco/gamedao-protocol" +edition = "2018" +license = "GPL-3.0-or-later" +description = "BattlePass pallet provides functionality to create, manage and participate in battlepasses." + +[dependencies] +serde = { version = "1.0.143", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } +sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } +sp-storage = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } +sp-io = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.43", default-features=false } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false } +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false, optional = true } + +pallet-nfts = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } + +orml-traits = { git = "https://github.com/open-web3-stack/open-runtime-module-library", branch= "polkadot-v0.9.43", default-features = false } + +gamedao-control = { package = "gamedao-control", path = "../control", default-features = false } +gamedao-traits = { package = "gamedao-traits", path = "../traits", default-features = false } + +[dev-dependencies] +sp-core = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.43" } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } + +pallet-balances = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } +orml-currencies = { git = "https://github.com/open-web3-stack/open-runtime-module-library", branch= "polkadot-v0.9.43", default-features = false } +orml-tokens = { git = "https://github.com/open-web3-stack/open-runtime-module-library", branch= "polkadot-v0.9.43", default-features = false } +orml-traits = { git = "https://github.com/open-web3-stack/open-runtime-module-library", branch= "polkadot-v0.9.43", default-features = false } + +[features] +default = ['std'] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "gamedao-traits/runtime-benchmarks", + "gamedao-control/runtime-benchmarks", + "pallet-nfts/runtime-benchmarks" +] +std = [ + "serde/std", + 'codec/std', + "scale-info/std", + + "frame-support/std", + "frame-system/std", + "frame-benchmarking/std", + + "sp-std/std", + + "pallet-balances/std", + + "orml-traits/std", + "orml-tokens/std", + "orml-currencies/std", + + "pallet-nfts/std", + + "gamedao-control/std", + "gamedao-traits/std", +] +try-runtime = ["frame-support/try-runtime"] diff --git a/battlepass/readme.md b/battlepass/readme.md new file mode 100644 index 000000000..f119b1d89 --- /dev/null +++ b/battlepass/readme.md @@ -0,0 +1,142 @@ +# BattlePass Beta + + + +## Definitions + Dictionary + + - Guild -> A group of people gaming together. + also Clan, Team, Crew, Squad, Party + + - BattlePass (BP) -> A subscription based engagement protocol for gaming guilds + + - Quest (Q) -> A group of tasks, bounties, etc on related games and media, like discord, twitter, twitch, Fortnite, CS, ... resulting in receiving points (XP,REP,TRUST,...) upon verifiable completion of its parts. + + - Quest Progress (QP) -> ( NonBinary / Float ) Fulfilled 0...1 of Quest Q + + - Points (P) -> Named individually in a guild, reference score based on the following + + - XP -> Experience Points based on tasks fulfilled in context of a Quest and BattlePass, may reset each season + + - REP -> Reputation Points based on street cred of a user on global level + + - TRUST -> Trust level based on verifiable credentials, e.g. connect twitter, discord, twitch, web3name, etc + + - Level (L) -> achieved by collecting points + + - Achievement -> + 1 NFT proof of achievement + 2 this enables account controller to redeem a reward + 3 to mint an achievement we use a template struct defining -> + a. data struct for the immutable nft part / configuration + b. data struct for the im/mutable payload part + + +## Overview + +BattlePass is a subscription based engagement protocol for gaming guilds. It enables claiming and dropping rewards based on completing quests. These achievements are based on verifiable activity and contribute to xp, rep, trust in SENSE. + + +## BattlePass for DAOs + + 1. It can be invoked by a DAO through staking (1000) GAME token on the protocol treasury. + + 2. Staking automatically enables the BattlePass section in a DAO. + + 3. The DAO invoking wishing to enable BattlePass needs verified credentials and be in good standing: + - (XP) > (100) + - (REP) > (100) + - (TRUST) > (100) + - MEMBERS > 10 + +BattlePass starts operating immediately when the staking deposit has been transferred. To make proper use of it, some defaults are in place to make it work: + + - By default a BattlePass utilizes xp,rep,trust in relation to a DAO + + - therefore a DAO specific map for xp,rep,trust has to be setup on DAO creation + + - a BP operates in seasons. a season is by default 100 days based on the respective blocktime of the host chain this results with 6s blocktime in in 10 * 60 * 24 = 14400 blocks per day and 144000 blocks per season. + + - after a season has finished, a new season starts, resetting the achieved score + + - a user subscribes for a BP over one to n seasons + + - a user collects points during the season based on their verifiable activity in games, on socials, etc + + - based on a score table, user is allowed to claim rewards from a level L and can use points P to claim rewards up to level L + + - P are calculated based on a formula like: + + *** DRAFT *** + + P = + MAP * + ( 1 * subscription_mul ) + -> local + 0 * ( -> activated? + 1 + + XP( season ) * -> local + ( 1 + REP / 100 ) * -> global + ( 1 + TRUST * 100 ) -> global + ) + + - where MAP is the individual mapping for achievements / levels, etc + + everybody can play, but only with a subscription you get the multiplier to make real progress into the claimable scores + + *** DRAFT *** + + - rewards will be delivered as nft based collectable, like + - proof of achievement + - proof of participation + - ticket + - collectable + ... + + - DAO needs to create a reward map based on a + score_threshold ST, + a reward object R, + a price in points P: + + { ST , ( R , P ) } + +example: 1000, ( item_drop_dragonball_nft, 0 ) + +## BattlePass for gamers and creators + +Battlepass provides the following functions: + + 0. Signup with discord **and** wallet + + 1. Connecting the following social accounts to start collecting points + - discord -> 1st poc e2e + - twitter -> 2nd due to relevance + - twitch -> tbd + - polkadot compatible wallet -> talisman preferred + - suggestions welcome + + 2. One-time or subscription payment for a Battlepass settled through PSP in FIAT or directly via stablecoin (USDT) + + 3. Browsing the rewards showing + - xp bar + - cta to join if not joined + - levels and/or required points to claim reward x + - enabling claim button when score is sufficient + + 4. Browsing Quests and Quest Progress + + +## Pallet + +### Sense + + map global ( season, xp , rep, trust ) + map local ( season, xp , rep, trust ) + +### BattlePass + +### Control + + + +### + +© 2022 GameDAO AG \ No newline at end of file diff --git a/battlepass/src/benchmarking.rs b/battlepass/src/benchmarking.rs new file mode 100644 index 000000000..9262d872e --- /dev/null +++ b/battlepass/src/benchmarking.rs @@ -0,0 +1,260 @@ +#![cfg(feature = "runtime-benchmarks")] + +use super::*; +use crate::Pallet as BPass; + +use frame_benchmarking::{benchmarks, impl_benchmark_test_suite, whitelisted_caller, account}; +use frame_system::RawOrigin; +use sp_runtime::{DispatchError, traits::SaturatedConversion}; +use sp_std::vec; + +const DEPOSIT_AMOUNT: u128 = 10_000_000_000_000_000_000; + +/// Fund account with tokens, needed for org and campaign interactions +fn fund_account(account_id: &T::AccountId) -> Result<(), DispatchError> { + let balance_amount: T::Balance = DEPOSIT_AMOUNT.saturated_into(); + ::Currency::deposit(T::NativeTokenId::get(), &account_id, balance_amount)?; + ::Currency::deposit(T::ProtocolTokenId::get(), &account_id, balance_amount)?; + Ok(()) +} + +/// Fund account with tokens, needed for org and battlepass interactions +fn get_funded_caller() -> Result { + let caller: T::AccountId = whitelisted_caller(); + fund_account::(&caller)?; + + Ok(caller) +} + + +fn get_org(caller: T::AccountId) -> T::Hash { + let org_id = T::ControlBenchmarkHelper::create_org(caller.clone()).unwrap(); + let treasury_id = T::Control::org_treasury_account(&org_id).unwrap(); + fund_account::(&treasury_id).unwrap(); + + org_id +} + +fn get_battlepass(caller: T::AccountId, org_id: T::Hash) -> T::Hash { + let str = BoundedVec::truncate_from(vec![1,2]); + let battlepass = types::Battlepass { + creator: caller.clone(), + org_id, + name: str.clone(), + cid: str.clone(), + season: 1, + price: 10, + collection_id: 0 + }; + + let _ = BPass::::create_battlepass(RawOrigin::Signed(caller.clone()).into(), org_id, battlepass.cid.clone(), battlepass.name.clone(), battlepass.price.clone()); + + ::Hashing::hash_of(&battlepass) +} + +fn activate_bpass(caller: T::AccountId, battlepass_id: T::Hash) { + let _ = BPass::::activate_battlepass(RawOrigin::Signed(caller).into(), battlepass_id); +} + +fn claim_bpass(caller: T::AccountId, battlepass_id: T::Hash) { + let _ = BPass::::claim_battlepass(RawOrigin::Signed(caller.clone()).into(), battlepass_id, caller, None); +} + +fn set_bpass_points(caller: T::AccountId, battlepass_id: T::Hash) { + let _ = BPass::::set_points(RawOrigin::Signed(caller.clone()).into(), battlepass_id, caller, 10); +} + +fn set_bpass_level(caller: T::AccountId, battlepass_id: T::Hash) { + let _ = BPass::::add_level(RawOrigin::Signed(caller.clone()).into(), battlepass_id, 1, 10); +} + +fn get_reward(caller: T::AccountId, battlepass_id: T::Hash) -> T::Hash { + let str = BoundedVec::truncate_from(vec![1,2]); + let reward = Reward{ + battlepass_id, + name: str.clone(), + cid: str.clone(), + level: 1, + transferable: true, + collection_id: 1 + }; + + let _ = BPass::::create_reward(RawOrigin::Signed(caller).into(), battlepass_id, reward.name.clone(), reward.cid.clone(), Some(10), reward.level.clone(), reward.transferable.clone()); + + ::Hashing::hash_of(&reward) +} + +fn set_bot(creator: T::AccountId, battlepass_id: T::Hash, bot: T::AccountId) { + let _ = BPass::::add_bot(RawOrigin::Signed(creator.clone()).into(), battlepass_id, bot); +} + + +benchmarks! { + + create_battlepass { + let caller: T::AccountId = get_funded_caller::()?; + let org_id = get_org::(caller.clone()); + let str = BoundedVec::truncate_from(vec![1,2]); + }: _(RawOrigin::Signed(caller), org_id, str.clone(), str.clone(), 10) + verify { + assert!(BattlepassInfoByOrg::::get(org_id).is_some()); + } + + update_battlepass { + let caller: T::AccountId = get_funded_caller::()?; + let bot: T::AccountId = account("bot", 0, 0); + let org_id = get_org::(caller.clone()); + let battlepass_id = get_battlepass::(caller.clone(), org_id); + let new_name = BoundedVec::truncate_from(b"new name".to_vec()); + let new_cid = BoundedVec::truncate_from(b"new cid".to_vec()); + let new_price = 20; + set_bot::(caller.clone(), battlepass_id, bot.clone()); + }: _(RawOrigin::Signed(bot), battlepass_id, Some(new_name.clone()), Some(new_cid.clone()), Some(new_price.clone())) + verify { + let battlepass = Battlepasses::::get(battlepass_id).unwrap(); + assert!(battlepass.name == new_name); + assert!(battlepass.cid == new_cid); + assert!(battlepass.price == new_price); + } + + claim_battlepass { + let caller: T::AccountId = get_funded_caller::()?; + let bot: T::AccountId = account("bot", 0, 0); + let org_id = get_org::(caller.clone()); + let battlepass_id = get_battlepass::(caller.clone(), org_id); + activate_bpass::(caller.clone(), battlepass_id); + set_bot::(caller.clone(), battlepass_id, bot.clone()); + }: _(RawOrigin::Signed(bot), battlepass_id, caller.clone(), None) + verify { + let collection_id = T::BattlepassHelper::collection(0); + let item_id = T::BattlepassHelper::item(0); + assert!( as InspectEnumerable>::items(&collection_id).any(|x| x == item_id)); + } + + activate_battlepass { + let caller: T::AccountId = get_funded_caller::()?; + let org_id = get_org::(caller.clone()); + let battlepass_id = get_battlepass::(caller.clone(), org_id); + }: _(RawOrigin::Signed(caller), battlepass_id) + verify { + assert!(BattlepassStates::::get(battlepass_id) == Some(BattlepassState::ACTIVE)); + } + + conclude_battlepass { + let caller: T::AccountId = get_funded_caller::()?; + let org_id = get_org::(caller.clone()); + let battlepass_id = get_battlepass::(caller.clone(), org_id); + activate_bpass::(caller.clone(), battlepass_id); + }: _(RawOrigin::Signed(caller), battlepass_id) + verify { + assert!(BattlepassStates::::get(battlepass_id) == Some(BattlepassState::ENDED)); + } + + set_points { + let caller: T::AccountId = get_funded_caller::()?; + let bot: T::AccountId = account("bot", 0, 0); + let org_id = get_org::(caller.clone()); + let battlepass_id = get_battlepass::(caller.clone(), org_id); + activate_bpass::(caller.clone(), battlepass_id); + claim_bpass::(caller.clone(), battlepass_id); + set_bot::(caller.clone(), battlepass_id, bot.clone()); + }: _(RawOrigin::Signed(bot), battlepass_id, caller.clone(), 10) + verify { + assert!(Points::::get(battlepass_id, caller) == 10); + } + + create_reward { + let caller: T::AccountId = get_funded_caller::()?; + let bot: T::AccountId = account("bot", 0, 0); + let org_id = get_org::(caller.clone()); + let battlepass_id = get_battlepass::(caller.clone(), org_id); + let str = BoundedVec::truncate_from(vec![1,2]); + activate_bpass::(caller.clone(), battlepass_id); + set_bot::(caller.clone(), battlepass_id, bot.clone()); + }: _(RawOrigin::Signed(bot), battlepass_id, str.clone(), str.clone(), Some(10), 1, false) + verify { + assert!(Rewards::::iter_keys().count() == 1); + } + + update_reward { + let caller: T::AccountId = get_funded_caller::()?; + let bot: T::AccountId = account("bot", 0, 0); + let org_id = get_org::(caller.clone()); + let battlepass_id = get_battlepass::(caller.clone(), org_id); + let reward_id = get_reward::(caller.clone(), battlepass_id); + let new_name = BoundedVec::truncate_from(b"new name".to_vec()); + let new_cid = BoundedVec::truncate_from(b"new cid".to_vec()); + let new_transferable = false; + set_bot::(caller.clone(), battlepass_id, bot.clone()); + }: _(RawOrigin::Signed(bot), reward_id, Some(new_name.clone()), Some(new_cid.clone()), Some(new_transferable.clone())) + verify { + let reward = Rewards::::get(reward_id).unwrap(); + assert!(reward.name == new_name); + assert!(reward.cid == new_cid); + assert!(reward.transferable == new_transferable); + } + + disable_reward { + let caller: T::AccountId = get_funded_caller::()?; + let bot: T::AccountId = account("bot", 0, 0); + let org_id = get_org::(caller.clone()); + let battlepass_id = get_battlepass::(caller.clone(), org_id); + let reward_id = get_reward::(caller.clone(), battlepass_id); + set_bot::(caller.clone(), battlepass_id, bot.clone()); + }: _(RawOrigin::Signed(bot), reward_id) + verify { + assert!(RewardStates::::get(reward_id) == Some(RewardState::INACTIVE)); + } + + claim_reward { + let caller: T::AccountId = get_funded_caller::()?; + let bot: T::AccountId = account("bot", 0, 0); + let org_id = get_org::(caller.clone()); + let battlepass_id = get_battlepass::(caller.clone(), org_id); + activate_bpass::(caller.clone(), battlepass_id); + claim_bpass::(caller.clone(), battlepass_id); + set_bpass_points::(caller.clone(), battlepass_id); + set_bpass_level::(caller.clone(), battlepass_id); + set_bot::(caller.clone(), battlepass_id, bot.clone()); + let reward_id = get_reward::(caller.clone(), battlepass_id); + }: _(RawOrigin::Signed(bot), reward_id, caller.clone(), None) + verify { + assert!(ClaimedRewards::::get(reward_id, caller).is_some()); + } + + add_level { + let caller: T::AccountId = get_funded_caller::()?; + let bot: T::AccountId = account("bot", 0, 0); + let org_id = get_org::(caller.clone()); + let battlepass_id = get_battlepass::(caller.clone(), org_id); + set_bot::(caller.clone(), battlepass_id, bot.clone()); + }: _(RawOrigin::Signed(bot), battlepass_id, 1, 10) + verify { + assert!(Levels::::get(battlepass_id, 1) == Some(10)); + } + + remove_level { + let caller: T::AccountId = get_funded_caller::()?; + let bot: T::AccountId = account("bot", 0, 0); + let org_id = get_org::(caller.clone()); + let battlepass_id = get_battlepass::(caller.clone(), org_id); + set_bpass_level::(caller.clone(), battlepass_id); + set_bot::(caller.clone(), battlepass_id, bot.clone()); + }: _(RawOrigin::Signed(bot), battlepass_id, 1) + verify { + assert!(Levels::::get(battlepass_id, 1) == None); + } + + add_bot { + let caller: T::AccountId = get_funded_caller::()?; + let org_id = get_org::(caller.clone()); + let battlepass_id = get_battlepass::(caller.clone(), org_id); + }: _(RawOrigin::Signed(caller.clone()), battlepass_id, caller.clone()) + verify { + assert!(BattlepassInfoByOrg::::get(org_id).unwrap().bot == Some(caller)); + } + + impl_benchmark_test_suite!(BPass, crate::mock::new_test_ext(), crate::mock::Test); +} + + diff --git a/battlepass/src/lib.rs b/battlepass/src/lib.rs new file mode 100644 index 000000000..c77965b6f --- /dev/null +++ b/battlepass/src/lib.rs @@ -0,0 +1,1017 @@ +// _______ ________ ________ ________ ______ _______ _______ +// ╱╱ ╲╱ ╲╱ ╲╱ ╲_╱ ╲╲╱ ╲╲╱ ╲╲ +// ╱╱ __╱ ╱ ╱ ╱ ╱╱ ╱╱ ╱╱ +// ╱ ╱ ╱ ╱ ╱ _╱ ╱ ╱ ╱ +// ╲________╱╲___╱____╱╲__╱__╱__╱╲________╱╲________╱╲___╱____╱╲________╱ +// +// This file is part of GameDAO Protocol. +// Copyright (C) 2018-2022 GameDAO AG. +// SPDX-License-Identifier: Apache-2.0 + +//! BATTLEPASS +//! This pallet provides functionality to create, manage and participate in battlepasses. +#![cfg_attr(not(feature = "std"), no_std)] + +pub use pallet::*; +use frame_support::{pallet_prelude::*, transactional, dispatch::RawOrigin}; +use frame_support::traits::tokens::nonfungibles_v2::InspectEnumerable; +use frame_system::pallet_prelude::*; +use pallet_nfts::{CollectionConfig, CollectionSettings, MintSettings, NextCollectionId, Incrementable}; +use sp_std::convert::TryInto; +use sp_runtime::traits::{AtLeast32BitUnsigned, Hash, StaticLookup}; +use gamedao_traits::ControlTrait; +#[cfg(feature = "runtime-benchmarks")] +use gamedao_traits::ControlBenchmarkingTrait; +use orml_traits::{MultiCurrency, MultiReservableCurrency}; + +pub mod types; +pub use types::*; + +mod mock; +mod tests; +mod benchmarking; + +pub mod weights; +pub use weights::WeightInfo; + +pub type String = BoundedVec::StringLimit>; + +pub trait BattlepassHelper { + fn collection(i: u32) -> CollectionId; + fn item(i: u32) -> ItemId; +} + +pub struct BpHelper; + +impl, ItemId: From> BattlepassHelper for BpHelper { + fn collection(id: u32) -> CollectionId { + id.into() + } + fn item(id: u32) -> ItemId { + id.into() + } +} + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config + pallet_nfts::Config { + type RuntimeEvent: From> + + IsType<::RuntimeEvent> + + Into<::RuntimeEvent>; + + /// The units in which we record balances. + type Balance: Member + + Parameter + + AtLeast32BitUnsigned + + Default + + Copy + + MaybeSerializeDeserialize + + MaxEncodedLen + + TypeInfo; + + /// The currency ID type + type CurrencyId: Member + + Parameter + + Copy + + MaybeSerializeDeserialize + + MaxEncodedLen + + TypeInfo; + + /// Multi-currency support for asset management. + type Currency: MultiCurrency + + MultiReservableCurrency; + + type Control: ControlTrait; + + #[cfg(feature = "runtime-benchmarks")] + type ControlBenchmarkHelper: ControlBenchmarkingTrait; + + type BattlepassHelper: BattlepassHelper; + + /// The maximum length of a name, cid strings stored on-chain. + #[pallet::constant] + type StringLimit: Get; + + /// The CurrencyId which is used as a native token. + #[pallet::constant] + type NativeTokenId: Get; + + /// The CurrencyId which is used as a protokol token. + #[pallet::constant] + type ProtocolTokenId: Get; + + /// Weight information for extrinsics in this module. + type WeightInfo: WeightInfo; + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// New BattlePass created + BattlepassCreated { + org_id: T::Hash, + battlepass_id: T::Hash, + season: u32 + }, + + /// BattlePass updated + BattlepassUpdated { + battlepass_id: T::Hash, + name: Option>, + cid: Option>, + price: Option + }, + + /// BattlePass claimed + BattlepassClaimed { + by_who: T::AccountId, + for_who: T::AccountId, + org_id: T::Hash, + battlepass_id: T::Hash, + nft_id: T::ItemId + }, + + /// BattlePass activated + BattlepassActivated { + by_who: T::AccountId, + org_id: T::Hash, + battlepass_id: T::Hash + }, + + /// BattlePass ended + BattlepassEnded { + by_who: T::AccountId, + org_id: T::Hash, + battlepass_id: T::Hash + }, + + /// Points updated for user + PointsUpdated { + by_who: T::AccountId, + for_who: T::AccountId, + battlepass_id: T::Hash, + amount: u32 + }, + + /// New Reward created + RewardCreated { + reward_id: T::Hash, + battlepass_id: T::Hash, + level: u8 + }, + + /// Reward updated + RewardUpdated { + reward_id: T::Hash, + name: Option>, + cid: Option>, + transferable: Option + }, + + /// Reward claimed by user + RewardClaimed { + reward_id: T::Hash, + claimer: T::AccountId, + collection_id: T::CollectionId, + nft_id: T::ItemId + }, + + /// Reward state updated + RewardStateUpdated { + reward_id: T::Hash, + state: RewardState + }, + + /// Achievement level added for Battlepass + LevelAdded { + battlepass_id: T::Hash, + level: u8, + points: u32 + }, + + /// Achievement level removed from Battlepass + LevelRemoved { + battlepass_id: T::Hash, + level: u8 + }, + + BotAdded { + battlepass_id: T::Hash, + bot: T::AccountId + } + } + + #[pallet::error] + pub enum Error { + AuthorizationError, + BattlepassExists, + BattlepassOwnershipExists, + BattlepassOwnershipDoesntExist, + BattlepassUnknown, + BattlepassStateUnknown, + BattlepassStateWrong, + BattlepassInfoUnknown, + LevelNotReached, + LevelUnknown, + NoAvailableCollectionId, + NoAvailableNftId, + NoChangesProvided, + OrgPrimeUnknown, + OrgUnknownOrInactive, + RewardClaimed, + RewardInactive, + RewardUnknown, + RewardStateUnknown, + } + + /// Battlepass by its id. + /// + /// Battlepasses: map Hash => Battlepass + #[pallet::storage] + #[pallet::getter(fn get_battlepass)] + pub(super) type Battlepasses = StorageMap<_, Blake2_128Concat, T::Hash, Battlepass, T::CollectionId>, OptionQuery>; + + /// Battlepass state. + /// + /// BattlepassStates: map Hash => BattlepassState + #[pallet::storage] + #[pallet::getter(fn get_battlepass_state)] + pub(super) type BattlepassStates = StorageMap<_, Blake2_128Concat, T::Hash, BattlepassState, OptionQuery>; + + /// Battlepass info by organization. + /// + /// BattlepassInfoByOrg: map Hash => BattlepassInfo + #[pallet::storage] + pub(super) type BattlepassInfoByOrg = StorageMap<_, Blake2_128Concat, T::Hash, BattlepassInfo, OptionQuery>; + + /// Total earned Points for users per each Battlepass. + /// + /// Points: map (Hash, AccountId) => u32 + #[pallet::storage] + #[pallet::getter(fn get_points)] + pub(super) type Points = StorageDoubleMap<_, + Blake2_128Concat, T::Hash, + Blake2_128Concat, T::AccountId, + u32, + ValueQuery + >; + + /// Reward by its id. + /// + /// Rewards: map Hash => Reward + #[pallet::storage] + #[pallet::getter(fn get_reward)] + pub(super) type Rewards = StorageMap<_, Blake2_128Concat, T::Hash, Reward, T::CollectionId>, OptionQuery>; + + /// Reward state by its id. + /// + /// RewardStates: map Hash => RewardState + #[pallet::storage] + #[pallet::getter(fn get_reward_state)] + pub(super) type RewardStates = StorageMap<_, Blake2_128Concat, T::Hash, RewardState, OptionQuery>; + + /// Claimed Reward-NFT by user. + /// + /// ClaimedRewards: map (Hash, AccountId) => ItemId + #[pallet::storage] + #[pallet::getter(fn get_claimed_rewards)] + pub(super) type ClaimedRewards = StorageDoubleMap<_, + Blake2_128Concat, T::Hash, + Blake2_128Concat, T::AccountId, + T::ItemId, + OptionQuery + >; + + /// Achievement levels mapping for Battlepass + /// + /// Levels: map (Hash, u8) => u32 + #[pallet::storage] + #[pallet::getter(fn get_level)] + pub(super) type Levels = StorageDoubleMap<_, + Blake2_128Concat, T::Hash, + Blake2_128Concat, u8, + u32, + OptionQuery + >; + + /// A counter for created NFTs + /// + /// NftIndex: u32 + #[pallet::storage] + #[pallet::getter(fn get_nft_index)] + pub(super) type NftIndex = StorageValue<_, u32, ValueQuery>; + + #[pallet::call] + impl Pallet { + + /// Creates a Battlepass. + /// Also creates a new collection to store claimed Battlepass NFTs. + /// May be called only by Organization owner. + /// + /// Parameters: + /// - `org_id`: ID of the Organization for which to create a Battlepass. + /// - `name`: Battlepass name. + /// - `cid`: IPFS content identifier. + /// - `price`: Price for the Battlepass subscription. + #[pallet::call_index(0)] + #[pallet::weight(::WeightInfo::create_battlepass())] + #[transactional] + pub fn create_battlepass( + origin: OriginFor, + org_id: T::Hash, + name: String, + cid: String, + price: u16, + ) -> DispatchResult { + let creator = ensure_signed(origin)?; + // check if Org is active + ensure!(T::Control::is_org_active(&org_id), Error::::OrgUnknownOrInactive); + // check permissions (prime) + ensure!(Self::is_prime(&org_id, creator.clone())?, Error::::AuthorizationError); + let (battlepass_count, _, _) = Self::get_battlepass_info(&org_id); + let new_season = battlepass_count + 1; + + // Create a collection to store Battlepass NFTs + let collection_id = Self::create_collection(creator.clone(), None, cid.clone())?; + let battlepass_id = Self::do_create_battlepass(creator, org_id, name, cid, collection_id, price, new_season)?; + + Self::deposit_event(Event::BattlepassCreated { org_id, battlepass_id, season: new_season }); + + Ok(()) + } + + /// Updates Battlepass. + /// Also updates Collection's metadata if Battlepass CID has changed. + /// May be called only by Organization owner. + /// + /// Parameters: + /// - `battlepass_id`: ID of the Battlepass to update. + /// - `name`: Battlepass name. + /// - `cid`: IPFS content identifier. + /// - `price`: Price for the Battlepass subscription. + #[pallet::call_index(1)] + #[pallet::weight(::WeightInfo::update_battlepass())] + #[transactional] + pub fn update_battlepass( + origin: OriginFor, + battlepass_id: T::Hash, + name: Option>, + cid: Option>, + price: Option, + ) -> DispatchResult { + let creator = ensure_signed(origin)?; + // check if Battlepass exists + let mut battlepass = Self::get_battlepass(battlepass_id).ok_or(Error::::BattlepassUnknown)?; + // check if there is something to update + let name_changed = name.is_some() && name.clone().unwrap() != battlepass.name; + let cid_changed = cid.is_some() && cid.clone().unwrap() != battlepass.cid; + let price_changed = price.is_some() && price.unwrap() != battlepass.price; + ensure!( + name_changed || cid_changed || price_changed, + Error::::NoChangesProvided + ); + // check if Battlepass state is not ENDED + ensure!(!Self::check_battlepass_state(battlepass_id, BattlepassState::ENDED)?, Error::::BattlepassStateWrong); + // check if Org is active + ensure!(T::Control::is_org_active(&battlepass.org_id), Error::::OrgUnknownOrInactive); + // check permissions (prime) + ensure!(Self::is_prime_or_bot(&battlepass.org_id, creator)?, Error::::AuthorizationError); + + if name_changed { battlepass.name = name.clone().unwrap() } + if price_changed { battlepass.price = price.unwrap() } + if cid_changed { + battlepass.cid = cid.clone().unwrap(); + + let prime = T::Control::org_prime_account(&battlepass.org_id).ok_or(Error::::OrgPrimeUnknown)?; + Self::update_collection_metadata(prime, battlepass.collection_id, cid.clone().unwrap())?; + } + + Battlepasses::::insert(battlepass_id, battlepass); + + Self::deposit_event(Event::BattlepassUpdated { battlepass_id, name, cid, price }); + + Ok(()) + } + + /// Claims the Battlepass-NFT for user who joined the Battlepass. + /// This NFT may be used as a proof of a Battlepass membership. + /// May be called by Organization owner or by a specially dedicated for this purpose account (Bot). + /// + /// Parameters: + /// - `battlepass_id`: ID of the Battlepass for which to claim NFT. + /// - `claimer`: Account for which to claim NFT. + /// - `cid`: If provided, store it in NFT's metadata. Otherwise, Battlepass CID will be stored there. + #[pallet::call_index(2)] + #[pallet::weight(::WeightInfo::claim_battlepass())] + #[transactional] + pub fn claim_battlepass( + origin: OriginFor, + battlepass_id: T::Hash, + claimer: T::AccountId, + cid: Option> + ) -> DispatchResult { + let caller = ensure_signed(origin)?; + // check if Battlepass exists + let battlepass = Self::get_battlepass(battlepass_id).ok_or(Error::::BattlepassUnknown)?; + // check if Battlepass in ACTIVE state + ensure!(Self::check_battlepass_state(battlepass_id, BattlepassState::ACTIVE)?, Error::::BattlepassStateWrong); + // check if Org is active + let org_id = battlepass.org_id; + ensure!(T::Control::is_org_active(&org_id), Error::::OrgUnknownOrInactive); + // check permissions (prime, bot) + ensure!(Self::is_prime_or_bot(&org_id, caller.clone())?, Error::::AuthorizationError); + // check if user has access to Battlepass + ensure!(!Self::is_battlepass_member(claimer.clone(), battlepass.collection_id), Error::::BattlepassOwnershipExists); + + let nft_id = Self::do_claim_battlepass(battlepass.creator.clone(), claimer.clone(), battlepass.collection_id, cid.unwrap_or(battlepass.cid))?; + + Self::deposit_event(Event::BattlepassClaimed { by_who: caller, for_who: claimer, org_id, battlepass_id, nft_id }); + + Ok(()) + } + + /// Activates the Battlepass. + /// Can activate only Battlepass in DRAFT state. + /// May be called only by Organization owner. + /// + /// Parameters: + /// - `battlepass_id`: ID of the Battlepass to activate. + #[pallet::call_index(3)] + #[pallet::weight(::WeightInfo::activate_battlepass())] + #[transactional] + pub fn activate_battlepass( + origin: OriginFor, + battlepass_id: T::Hash, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + // check if Battlepass exists + let battlepass = Self::get_battlepass(battlepass_id).ok_or(Error::::BattlepassUnknown)?; + // check if Battlepass in DRAFT state + ensure!(Self::check_battlepass_state(battlepass_id, BattlepassState::DRAFT)?, Error::::BattlepassStateWrong); + // check if Org is active + ensure!(T::Control::is_org_active(&battlepass.org_id), Error::::OrgUnknownOrInactive); + // check permissions (is_prime_or_bot) + ensure!(Self::is_prime_or_bot(&battlepass.org_id, sender.clone())?, Error::::AuthorizationError); + // check if there is no active battlepass for the Org + let (_, maybe_active, _) = Self::get_battlepass_info(&battlepass.org_id); + ensure!(maybe_active.is_none(), Error::::BattlepassExists); + + Self::change_battlepass_state(battlepass.org_id, battlepass_id, BattlepassState::ACTIVE)?; + + Self::deposit_event(Event::BattlepassActivated { by_who: sender, org_id: battlepass.org_id, battlepass_id }); + + Ok(()) + } + + /// Concludes the Battlepass. + /// Can conclude only Battlepass in ACTIVE state. + /// After calling this extrinsic Battlepass state can not be changed any more. + /// May be called only by Organization owner. + /// + /// Parameters: + /// - `battlepass_id`: ID of the Battlepass to conclude. + #[pallet::call_index(4)] + #[pallet::weight(::WeightInfo::conclude_battlepass())] + #[transactional] + pub fn conclude_battlepass( + origin: OriginFor, + battlepass_id: T::Hash, + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + // check if Battlepass exists + let battlepass = Self::get_battlepass(battlepass_id).ok_or(Error::::BattlepassUnknown)?; + // check if Battlepass in ACTIVE state + ensure!(Self::check_battlepass_state(battlepass_id, BattlepassState::ACTIVE)?, Error::::BattlepassStateWrong); + // check permissions (is_prime_or_bot) + ensure!(Self::is_prime_or_bot(&battlepass.org_id, sender.clone())?, Error::::AuthorizationError); + + Self::change_battlepass_state(battlepass.org_id, battlepass_id, BattlepassState::ENDED)?; + + Self::deposit_event(Event::BattlepassEnded { by_who: sender, org_id: battlepass.org_id, battlepass_id }); + + Ok(()) + } + + /// Sets Battlepass Points for user. + /// So far no information about users' achievements is stored on chain. A separate trusted service (Bot) + /// should collect such info, process it, validate it and call this extrinsic if user's Points have been updated. + /// May be called only by Organization owner or by a specially dedicated for this purpose account (Bot). + /// + /// Parameters: + /// - `battlepass_id`: ID of the Battlepass. + /// - `account`: User's account for which to set Points. + /// - `amount`: Amount of Points to set. + #[pallet::call_index(5)] + #[pallet::weight(::WeightInfo::set_points())] + pub fn set_points( + origin: OriginFor, + battlepass_id: T::Hash, + account: T::AccountId, + amount: u32 + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + // check if Battlepass exists + let battlepass = Self::get_battlepass(battlepass_id).ok_or(Error::::BattlepassUnknown)?; + // check if Battlepass in ACTIVE state + ensure!(Self::check_battlepass_state(battlepass_id, BattlepassState::ACTIVE)?, Error::::BattlepassStateWrong); + // check if Org is active + ensure!(T::Control::is_org_active(&battlepass.org_id), Error::::OrgUnknownOrInactive); + // check permissions (prime, bot) + ensure!(Self::is_prime_or_bot(&battlepass.org_id, sender.clone())?, Error::::AuthorizationError); + // check if user has access to Battlepass + ensure!(Self::is_battlepass_member(account.clone(), battlepass.collection_id), Error::::BattlepassOwnershipDoesntExist); + + Points::::insert(battlepass_id, &account, amount); + + Self::deposit_event(Event::PointsUpdated { by_who: sender, for_who: account, battlepass_id, amount }); + + Ok(()) + } + + /// Creates a Reward Type for the Battlepass. + /// Also creates a new collection to store claimed Reward NFTs. + /// May be called only by Organization owner or by a specially dedicated for this purpose account (Bot). + /// + /// Parameters: + /// - `battlepass_id`: ID of the Battlepass to create a Reward for. + /// - `name`: Name of the Reward. + /// - `cid`: IPFS content identifier. + /// - `max`: Maximum number of claimed rewards this Reward Type may have. Unlimited if empty. + /// - `level`: Minimum Level user must reach to be able to claim this Reward Type. + /// - `transferable`: Specifies whether claimed Reward NFTs could be transferred (sold) to another account. + #[pallet::call_index(6)] + #[pallet::weight(::WeightInfo::create_reward())] + #[transactional] + pub fn create_reward( + origin: OriginFor, + battlepass_id: T::Hash, + name: String, + cid: String, + max: Option, + level: u8, + transferable: bool, + ) -> DispatchResult { + let caller = ensure_signed(origin)?; + // check if Battlepass exists + let battlepass = Self::get_battlepass(battlepass_id).ok_or(Error::::BattlepassUnknown)?; + // check if Battlepass is not ended + ensure!(!Self::check_battlepass_state(battlepass_id, BattlepassState::ENDED)?, Error::::BattlepassStateWrong); + // check if Org is active + ensure!(T::Control::is_org_active(&battlepass.org_id), Error::::OrgUnknownOrInactive); + // check permissions (prime, bot) + ensure!(Self::is_prime_or_bot(&battlepass.org_id, caller)?, Error::::AuthorizationError); + + let prime = T::Control::org_prime_account(&battlepass.org_id).ok_or(Error::::OrgPrimeUnknown)?; + let collection_id = Self::create_collection(prime, max, cid.clone())?; + let reward_id = Self::do_create_reward(battlepass_id, name, cid, level, transferable, collection_id)?; + + Self::deposit_event(Event::RewardCreated { reward_id, battlepass_id, level }); + + Ok(()) + } + + /// Updates Reward type. + /// Also updates Collection's metadata if Reward's CID has changed. + /// May be called only by Organization owner or by a specially dedicated for this purpose account (Bot). + /// + /// Parameters: + /// - `reward_id`: ID of the Reward Type to be updated. + /// - `name`: Name of the Reward. + /// - `cid`: IPFS content identifier. + /// - `transferable`: Specifies whether claimed Reward NFTs could be transferred (sold) to another account. + #[pallet::call_index(7)] + #[pallet::weight(::WeightInfo::update_reward())] + pub fn update_reward( + origin: OriginFor, + reward_id: T::Hash, + name: Option>, + cid: Option>, + transferable: Option, + ) -> DispatchResult { + let caller = ensure_signed(origin)?; + // check if Reward exists + let mut reward = Self::get_reward(reward_id).ok_or(Error::::RewardUnknown)?; + // check if there is something to update + let name_changed = name.is_some() && name.clone().unwrap() != reward.name; + let cid_changed = cid.is_some() && cid.clone().unwrap() != reward.cid; + let transferable_changed = transferable.is_some() && transferable.unwrap() != reward.transferable; + ensure!( + name_changed || cid_changed || transferable_changed, + Error::::NoChangesProvided + ); + // check if Reward is active + ensure!(Self::check_reward_state(reward_id, RewardState::ACTIVE)?, Error::::RewardInactive); + // check if Battlepass exists + let battlepass = Self::get_battlepass(reward.battlepass_id).ok_or(Error::::BattlepassUnknown)?; + // check if Battlepass is not ended + ensure!(!Self::check_battlepass_state(reward.battlepass_id, BattlepassState::ENDED)?, Error::::BattlepassStateWrong); + // check if Org is active + ensure!(T::Control::is_org_active(&battlepass.org_id), Error::::OrgUnknownOrInactive); + // check permissions (prime, bot) + ensure!(Self::is_prime_or_bot(&battlepass.org_id, caller)?, Error::::AuthorizationError); + + if name_changed { reward.name = name.clone().unwrap() }; + if transferable_changed { reward.transferable = transferable.unwrap() }; + if cid_changed { + reward.cid = cid.clone().unwrap(); + + let prime = T::Control::org_prime_account(&battlepass.org_id).ok_or(Error::::OrgPrimeUnknown)?; + Self::update_collection_metadata(prime, reward.collection_id, cid.clone().unwrap())?; + } + + Rewards::::insert(reward_id, reward); + + Self::deposit_event(Event::RewardUpdated { reward_id, name, cid, transferable }); + + Ok(()) + } + + /// Disables the Reward Type. + /// After calling this extrinsic Reward Type state can not be changed any more. + /// May be called only by Organization owner or by a specially dedicated for this purpose account (Bot). + /// + /// Parameters: + /// - `reward_id`: ID of the Reward Type to be disabled. + #[pallet::call_index(8)] + #[pallet::weight(::WeightInfo::disable_reward())] + pub fn disable_reward( + origin: OriginFor, + reward_id: T::Hash + ) -> DispatchResult { + let caller = ensure_signed(origin)?; + // check if Reward exists + let reward = Self::get_reward(reward_id).ok_or(Error::::RewardUnknown)?; + // check if Reward is active + ensure!(Self::check_reward_state(reward_id, RewardState::ACTIVE)?, Error::::RewardInactive); + // check if Battlepass exists + let battlepass = Self::get_battlepass(reward.battlepass_id).ok_or(Error::::BattlepassUnknown)?; + // check permissions (prime, bot) + ensure!(Self::is_prime_or_bot(&battlepass.org_id, caller)?, Error::::AuthorizationError); + + let state = RewardState::INACTIVE; + + RewardStates::::insert(reward_id, state.clone()); + + Self::deposit_event(Event::RewardStateUpdated {reward_id, state} ); + + Ok(()) + } + + /// Claims a reward for user. + /// Mints a Reward NFT which may be used as a proof of a Reward posession. + /// User must be eligible for the Reward Type to be able to claim it. Eligibility criteria are: + /// - must be an Organization member. + /// - must be a Battlepass member (posess a valid Battlepass NFT). + /// - required achievement Level must be reached. + /// May be called by Organization owner or by a specially dedicated for this purpose account (Bot). + /// + /// Parameters: + /// - `reward_id`: ID of the Reward Type to claim. + /// - `claimer`: User account for who to claim the reward. + /// - `cid`: If provided, store it in NFT's metadata. Otherwise, Reward CID will be stored there. + #[pallet::call_index(9)] + #[pallet::weight(::WeightInfo::claim_reward())] + #[transactional] + pub fn claim_reward( + origin: OriginFor, + reward_id: T::Hash, + claimer: T::AccountId, + cid: Option> + ) -> DispatchResult { + let caller = ensure_signed(origin)?; + // check if Reward exists + let reward = Self::get_reward(reward_id).ok_or(Error::::RewardUnknown)?; + // check if Reward is active + ensure!(Self::check_reward_state(reward_id, RewardState::ACTIVE)?, Error::::RewardInactive); + // check if Reward has not been claimed yet + ensure!(!ClaimedRewards::::contains_key(reward_id, &claimer), Error::::RewardClaimed); + // check if Battlepass exists + let battlepass = Self::get_battlepass(reward.battlepass_id).ok_or(Error::::BattlepassUnknown)?; + let creator = battlepass.creator.clone(); + // check if Battlepass in ACTIVE state + ensure!(Self::check_battlepass_state(reward.battlepass_id, BattlepassState::ACTIVE)?, Error::::BattlepassStateWrong); + // check if Org is active + ensure!(T::Control::is_org_active(&battlepass.org_id), Error::::OrgUnknownOrInactive); + // check permissions (prime or bot) + ensure!(Self::is_prime_or_bot(&battlepass.org_id, caller)?, Error::::AuthorizationError); + // check if user has access to Battlepass + ensure!(Self::is_battlepass_member(claimer.clone(), battlepass.collection_id), Error::::BattlepassOwnershipDoesntExist); + + // check if user has reached the required Level + ensure!(Self::is_level_reached(&reward.battlepass_id, &claimer, reward.level), Error::::LevelNotReached); + + let nft_id = Self::do_claim_reward(creator, claimer.clone(), reward_id, reward.collection_id, cid.unwrap_or(reward.cid))?; + + Self::deposit_event(Event::RewardClaimed {reward_id, claimer, collection_id: reward.collection_id, nft_id} ); + + Ok(()) + } + + /// Adds a new achievement Level. + /// May be called only by Organization owner or by a specially dedicated for this purpose account (Bot). + /// + /// Parameters: + /// - `battlepass_id`: ID of the Battlepass to add a Level for. + /// - `level`: Achievement Level. + /// - `points`: Amount of Points needed to reach the Level. + #[pallet::call_index(10)] + #[pallet::weight(::WeightInfo::add_level())] + pub fn add_level( + origin: OriginFor, + battlepass_id: T::Hash, + level: u8, + points: u32 + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + // check if Battlepass exists + let battlepass = Self::get_battlepass(battlepass_id).ok_or(Error::::BattlepassUnknown)?; + // check if Battlepass is not ended + ensure!(!Self::check_battlepass_state(battlepass_id, BattlepassState::ENDED)?, Error::::BattlepassStateWrong); + // check if Org is active + ensure!(T::Control::is_org_active(&battlepass.org_id), Error::::OrgUnknownOrInactive); + // check permissions (prime, bot) + ensure!(Self::is_prime_or_bot(&battlepass.org_id, sender)?, Error::::AuthorizationError); + + Levels::::insert(battlepass_id, level, points); + + Self::deposit_event(Event::LevelAdded { battlepass_id, level, points } ); + + Ok(()) + } + + /// Removes achievement Level. + /// May be called only by Organization owner or by a specially dedicated for this purpose account (Bot). + /// + /// Parameters: + /// - `battlepass_id`: ID of the Battlepass to remove a Level for. + /// - `level`: Achievement Level. + #[pallet::call_index(11)] + #[pallet::weight(::WeightInfo::remove_level())] + pub fn remove_level( + origin: OriginFor, + battlepass_id: T::Hash, + level: u8 + ) -> DispatchResult { + let sender = ensure_signed(origin)?; + // check if Battlepass exists + let battlepass = Self::get_battlepass(battlepass_id).ok_or(Error::::BattlepassUnknown)?; + // check if Battlepass is not ended + ensure!(!Self::check_battlepass_state(battlepass_id, BattlepassState::ENDED)?, Error::::BattlepassStateWrong); + // check if Org is active + ensure!(T::Control::is_org_active(&battlepass.org_id), Error::::OrgUnknownOrInactive); + // check permissions (prime, bot) + ensure!(Self::is_prime_or_bot(&battlepass.org_id, sender)?, Error::::AuthorizationError); + // check if Level exists + ensure!(Levels::::contains_key(battlepass_id, level), Error::::LevelUnknown); + + Levels::::remove(battlepass_id, level); + + Self::deposit_event(Event::LevelRemoved { battlepass_id, level } ); + + Ok(()) + } + + /// Adds for a Battlepass a special trusted account (Bot) which will have a permission to update users' Points. + /// May be called only by Organization owner. + /// + /// Parameters: + /// - `battlepass_id`: ID of the Battlepass to add a Bot for. + /// - `bot`: Trusted Account ID. + #[pallet::call_index(12)] + #[pallet::weight(::WeightInfo::add_bot())] + pub fn add_bot( + origin: OriginFor, + battlepass_id: T::Hash, + bot: T::AccountId + ) -> DispatchResult { + // check if Battlepass exists + let battlepass = Self::get_battlepass(battlepass_id).ok_or(Error::::BattlepassUnknown)?; + // check if Battlepass is not ended + ensure!(!Self::check_battlepass_state(battlepass_id, BattlepassState::ENDED)?, Error::::BattlepassStateWrong); + // check if Org is active + ensure!(T::Control::is_org_active(&battlepass.org_id), Error::::OrgUnknownOrInactive); + // check permissions (prime or root) + ensure!(Self::is_prime_or_root(&battlepass.org_id, origin)?, Error::::AuthorizationError); + + BattlepassInfoByOrg::::try_mutate(battlepass.org_id, |info| -> Result<(), DispatchError> { + if let Some(inf) = info { + inf.bot = Some(bot.clone()); + Ok(()) + } else { + Err(Error::::BattlepassInfoUnknown)? + } + })?; + + Self::deposit_event(Event::BotAdded { battlepass_id, bot } ); + + Ok(()) + } + } +} + +impl Pallet { + + fn is_prime(org_id: &T::Hash, who: T::AccountId) -> Result { + let prime = T::Control::org_prime_account(org_id).ok_or(Error::::OrgPrimeUnknown)?; + Ok(who == prime) + } + + fn is_bot(org_id: &T::Hash, who: T::AccountId) -> Result { + let (_, _, bot) = Self::get_battlepass_info(org_id); + Ok(Some(who) == bot) + } + + fn is_prime_or_bot(org_id: &T::Hash, who: T::AccountId) -> Result { + Ok(Self::is_prime(org_id, who.clone())? || Self::is_bot(org_id, who)?) + } + + fn is_prime_or_root(org_id: &T::Hash, who: T::RuntimeOrigin) -> Result { + match who.into() { + Ok(RawOrigin::Root) => Ok(true), + Ok(RawOrigin::Signed(t)) => { + Self::is_prime(org_id, t) + }, + _ => Ok(false) + } + } + + fn is_level_reached(battlepass_id: &T::Hash, account: &T::AccountId, level: u8) -> bool { + let user_points = Self::get_points(battlepass_id, account); + let levels = Levels::::iter_prefix(battlepass_id) + .filter( + |(lvl, pnt)| level == *lvl && user_points >= *pnt + ); + + levels.count() == 1 + } + + fn bump_nft_index() -> Result { + NftIndex::::try_mutate(|n| -> Result { + let id = *n; + ensure!(id != u32::max_value(), Error::::NoAvailableNftId); + *n += 1; + Ok(id) + }) + } + + fn create_collection(owner: T::AccountId, max: Option, cid: String) -> Result { + let metadata = BoundedVec::truncate_from(cid.into()); + let collection = NextCollectionId::::get().unwrap_or(T::CollectionId::initial_value()); + let origin = OriginFor::::from(RawOrigin::Signed(owner.clone())); + let config = CollectionConfig { + settings: CollectionSettings::all_enabled(), + max_supply: max, + mint_settings: MintSettings::default(), + }; + + pallet_nfts::Pallet::::do_create_collection( + collection, + owner.clone(), + owner.clone(), + config, + T::CollectionDeposit::get(), + pallet_nfts::Event::Created { collection, creator: owner.clone(), owner }, + )?; + + pallet_nfts::Pallet::::set_collection_metadata(origin, collection, metadata)?; + + Ok(collection) + } + + fn create_nft(creator: T::AccountId, for_who: T::AccountId, collection_id: T::CollectionId, nft_id: T::ItemId, metadata: BoundedVec::StringLimit>) -> DispatchResult { + let creator = OriginFor::::from(RawOrigin::Signed(creator)); + let for_who = T::Lookup::unlookup(for_who); + + pallet_nfts::Pallet::::mint(creator.clone(), collection_id, nft_id, for_who, None)?; + pallet_nfts::Pallet::::set_metadata(creator, collection_id, nft_id, metadata)?; + + Ok(()) + } + + fn update_collection_metadata(owner: T::AccountId, collection_id: T::CollectionId, cid: String) -> DispatchResult { + let origin = OriginFor::::from(RawOrigin::Signed(owner)); + let metadata = BoundedVec::truncate_from(cid.into()); + + pallet_nfts::Pallet::::set_collection_metadata(origin, collection_id, metadata)?; + + Ok(()) + } + + fn check_battlepass_state(battlepass_id: T::Hash, state: BattlepassState) -> Result { + let current_state = Self::get_battlepass_state(battlepass_id).ok_or(Error::::BattlepassStateUnknown)?; + + Ok(current_state == state) + } + + fn check_reward_state(reward_id: T::Hash, state: RewardState) -> Result { + let current_state = Self::get_reward_state(reward_id).ok_or(Error::::RewardStateUnknown)?; + + Ok(current_state == state) + } + + fn get_battlepass_info(org_id: &T::Hash) -> (u32, Option, Option) { + if let Some(bp_info) = BattlepassInfoByOrg::::get(org_id) { + (bp_info.count, bp_info.active, bp_info.bot) + } else { + (0, None, None) + } + } + + fn do_create_battlepass(creator: T::AccountId, org_id: T::Hash, name: String, cid: String, collection_id: T::CollectionId, price: u16, season:u32) -> Result { + let battlepass: Battlepass, T::CollectionId> = Battlepass { + creator, + org_id, + name, + cid, + season, + collection_id, + price + }; + let battlepass_id = ::Hashing::hash_of(&battlepass); + + Battlepasses::::insert(battlepass_id, battlepass); + BattlepassStates::::insert(battlepass_id, BattlepassState::DRAFT); + if season == 1 { + BattlepassInfoByOrg::::insert(org_id, BattlepassInfo{count: season, active: None, bot: None}); + } else { + BattlepassInfoByOrg::::try_mutate(org_id, |info| -> Result<(), DispatchError> { + if let Some(inf) = info { + inf.count = season; + } else { + return Err(Error::::BattlepassInfoUnknown)?; + } + Ok(()) + })?; + } + + Ok(battlepass_id) + } + + fn do_claim_battlepass(creator: T::AccountId, for_who: T::AccountId, collection_id: T::CollectionId, cid: String) -> Result { + let nft_index = Self::bump_nft_index()?; + let nft_id: T::ItemId = T::BattlepassHelper::item(nft_index); + let metadata = BoundedVec::truncate_from(cid.into()); + + // Create Battlepass NFT + Self::create_nft(creator, for_who, collection_id, nft_id, metadata)?; + + Ok(nft_id) + } + + fn change_battlepass_state(org_id: T::Hash, battlepass_id: T::Hash, state: BattlepassState) -> DispatchResult { + let active_battlepass = if state == BattlepassState::ACTIVE { Some(battlepass_id) } else { None }; + + BattlepassStates::::insert(battlepass_id, state); + BattlepassInfoByOrg::::try_mutate(org_id, |info| -> Result<(), DispatchError> { + if let Some(inf) = info { + inf.active = active_battlepass; + Ok(()) + } else { + Err(Error::::BattlepassInfoUnknown)? + } + })?; + + Ok(()) + } + + fn do_create_reward(battlepass_id: T::Hash, name: String, cid: String, level: u8, transferable: bool, collection_id: T::CollectionId) -> Result { + let reward = Reward{ + battlepass_id, + name, + cid, + level, + transferable, + collection_id + }; + let reward_id = ::Hashing::hash_of(&reward); + + Rewards::::insert(reward_id, reward); + RewardStates::::insert(reward_id, RewardState::ACTIVE); + + Ok(reward_id) + } + + fn do_claim_reward(creator: T::AccountId, for_who: T::AccountId, reward_id: T::Hash, collection_id: T::CollectionId, cid: String) -> Result { + let nft_index = Self::bump_nft_index()?; + let nft_id = T::BattlepassHelper::item(nft_index); + let metadata = BoundedVec::truncate_from(cid.into()); + + // Create Reward NFT + Self::create_nft(creator, for_who.clone(), collection_id, nft_id, metadata)?; + + ClaimedRewards::::insert(reward_id, &for_who, nft_id); + + Ok(nft_id) + } + + fn is_battlepass_member(account: T::AccountId, bp_collection_id: T::CollectionId) -> bool { + let bp_owned_count = as InspectEnumerable>::owned_in_collection(&bp_collection_id, &account).count(); + bp_owned_count > 0 + } +} diff --git a/battlepass/src/mock.rs b/battlepass/src/mock.rs new file mode 100644 index 000000000..74e0f0f3c --- /dev/null +++ b/battlepass/src/mock.rs @@ -0,0 +1,265 @@ +#![cfg(test)] + +use crate as gamedao_battlepass; +use frame_support::{construct_runtime, parameter_types, PalletId, + traits::{AsEnsureOriginWithArg, Nothing, GenesisBuild}, + pallet_prelude::*, +}; +use frame_system; +use pallet_nfts::PalletFeatures; +use sp_std::convert::{TryFrom, TryInto}; +use sp_core::H256; +use sp_runtime::{ + testing::{Header, TestSignature}, + traits::{BlakeTwo256, IdentityLookup, Verify, IdentifyAccount}, +}; +use orml_traits::parameter_type_with_key; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +// types +pub type Hash = H256; +pub type Balance = u128; +pub type BlockNumber = u64; +pub type CurrencyId = u32; +pub type Amount = i128; +pub type Signature = TestSignature; +pub type AccountPublic = ::Signer; +pub type AccountId = ::AccountId; + +/// Constants: +pub const MILLICENTS: Balance = 1_000_000_000; +pub const CENTS: Balance = 1_000 * MILLICENTS; +pub const DOLLARS: Balance = 100 * CENTS; +pub const NATIVE_TOKEN_ID: CurrencyId = 0; +pub const PROTOCOL_TOKEN_ID: CurrencyId = 1; +pub const PAYMENT_TOKEN_ID: CurrencyId = 2; + +// Org creator: +pub const ALICE: AccountId = 11; +// Bot +pub const BOT: AccountId = 333; +// Contributors: +pub const BOB: AccountId = 12; +pub const EVA: AccountId = 13; +pub const TOM: AccountId = 14; + +pub const INIT_BALANCE: Balance = 100 * DOLLARS; + +#[derive(Encode, Decode, Eq, PartialEq, Copy, Clone, RuntimeDebug, PartialOrd, Ord, MaxEncodedLen, TypeInfo)] +#[repr(u8)] +pub enum ReserveIdentifier { + CollatorSelection, + Nft, + TransactionPayment, + TransactionPaymentDeposit, + + // always the last, indicate number of variants + Count, +} + +// Configure a mock runtime to test the pallet. +construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + PalletBalances: pallet_balances::{Pallet, Call, Storage, Event}, + Tokens: orml_tokens::{Pallet, Storage, Event, Config}, + Currencies: orml_currencies::{Pallet, Call}, + Nfts: pallet_nfts::{Pallet, Call, Storage, Event}, + Control: gamedao_control, + Battlepass: gamedao_battlepass::{Pallet, Call, Event, Storage}, + } +); + +parameter_types! { + pub const BlockHashCount: u32 = 250; + pub const SS58Prefix: u8 = 42; +} + +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Index = u64; + type BlockNumber = u64; + type Hash = Hash; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = SS58Prefix; + type OnSetCode = (); + type MaxConsumers = ConstU32<128>; +} + +parameter_type_with_key! { + pub ExistentialDeposits: |_currency_id: CurrencyId| -> Balance { + Default::default() + }; +} + +parameter_types! { + pub const MaxReserves: u32 = ReserveIdentifier::Count as u32; +} + +impl orml_tokens::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type Amount = Amount; + type CurrencyId = CurrencyId; + type WeightInfo = (); + type ExistentialDeposits = ExistentialDeposits; + type CurrencyHooks = (); + type MaxLocks = (); + type MaxReserves = MaxReserves; + type ReserveIdentifier = ReserveIdentifier; + type DustRemovalWhitelist = Nothing; +} + +parameter_types! { + pub const ExistentialDeposit: Balance = 1; +} + +impl pallet_balances::Config for Test { + type Balance = Balance; + type DustRemoval = (); + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = frame_system::Pallet; + type MaxLocks = (); + type MaxReserves = (); + type ReserveIdentifier = ReserveIdentifier; + type WeightInfo = (); +} + +pub type AdaptedBasicCurrency = orml_currencies::BasicCurrencyAdapter; + +impl orml_currencies::Config for Test { + type MultiCurrency = Tokens; + type NativeCurrency = AdaptedBasicCurrency; + type GetNativeCurrencyId = (); + type WeightInfo = (); +} + +parameter_types! { + pub Features: PalletFeatures = PalletFeatures::all_enabled(); + pub const MaxAttributesPerCall: u32 = 10; + pub const ApprovalsLimit: u32 = 20; + pub const ItemAttributesApprovalsLimit: u32 = 20; + pub const MaxTips: u32 = 10; + pub const MaxDeadlineDuration: BlockNumber = 10000; + pub CollectionDeposit: Balance = 0; + pub ItemDeposit: Balance = 0; + pub const KeyLimit: u32 = 32; // Max 32 bytes per key + pub const ValueLimit: u32 = 64; // Max 64 bytes per value + pub MetadataDepositBase: Balance = 0; + pub MetadataDepositPerByte: Balance = 0; + pub const StringLimit: u32 = 64; +} + +impl pallet_nfts::Config for Test { + type RuntimeEvent = RuntimeEvent; + type CollectionId = u32; + type ItemId = u32; + type Currency = PalletBalances; + type ForceOrigin = frame_system::EnsureRoot; + type CollectionDeposit = CollectionDeposit; + type ItemDeposit = ItemDeposit; + type MetadataDepositBase = MetadataDepositBase; + type AttributeDepositBase = MetadataDepositBase; + type DepositPerByte = MetadataDepositPerByte; + type StringLimit = StringLimit; + type KeyLimit = KeyLimit; + type ValueLimit = ValueLimit; + type ApprovalsLimit = ApprovalsLimit; + type ItemAttributesApprovalsLimit = ItemAttributesApprovalsLimit; + type MaxTips = MaxTips; + type MaxDeadlineDuration = MaxDeadlineDuration; + type MaxAttributesPerCall = MaxAttributesPerCall; + type Features = Features; + type OffchainSignature = Signature; + type OffchainPublic = AccountPublic; + type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type Helper = (); + type CreateOrigin = AsEnsureOriginWithArg>; + type Locker = (); +} + +parameter_types! { + pub const NativeTokenId: CurrencyId = NATIVE_TOKEN_ID; + pub const ProtocolTokenId: CurrencyId = PROTOCOL_TOKEN_ID; + pub const PaymentTokenId: CurrencyId = PAYMENT_TOKEN_ID; + pub const MinimumDeposit: Balance = 5 * DOLLARS; + pub const ControlPalletId: PalletId = PalletId(*b"gd/cntrl"); + pub const MaxMembers: u32 = 1000; +} +impl gamedao_control::Config for Test { + type Balance = Balance; + type CurrencyId = CurrencyId; + type WeightInfo = (); + type RuntimeEvent = RuntimeEvent; + type Currency = Currencies; + type MaxMembers = MaxMembers; + type ProtocolTokenId = ProtocolTokenId; + type PaymentTokenId = PaymentTokenId; + type MinimumDeposit = MinimumDeposit; + type PalletId = ControlPalletId; + type StringLimit = ConstU32<256>; +} + +impl gamedao_battlepass::Config for Test { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type CurrencyId = CurrencyId; + type Currency = Currencies; + type Control = Control; + #[cfg(feature = "runtime-benchmarks")] + type ControlBenchmarkHelper = Control; + type BattlepassHelper = gamedao_battlepass::BpHelper; + type StringLimit = StringLimit; + type NativeTokenId = NativeTokenId; + type ProtocolTokenId = ProtocolTokenId; + type WeightInfo = (); +} + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + orml_tokens::GenesisConfig:: { + balances: vec![ + // ALICE org creator + (ALICE, NATIVE_TOKEN_ID, INIT_BALANCE), + (ALICE, PROTOCOL_TOKEN_ID, INIT_BALANCE), + (ALICE, PAYMENT_TOKEN_ID, INIT_BALANCE), + + // Contributors + (BOB, PAYMENT_TOKEN_ID, INIT_BALANCE), + (EVA, PAYMENT_TOKEN_ID, INIT_BALANCE), + (1, PAYMENT_TOKEN_ID, INIT_BALANCE), + (2, PAYMENT_TOKEN_ID, INIT_BALANCE), + (3, PAYMENT_TOKEN_ID, INIT_BALANCE), + (4, PAYMENT_TOKEN_ID, INIT_BALANCE), + (5, PAYMENT_TOKEN_ID, INIT_BALANCE), + ], + } + .assimilate_storage(&mut t) + .unwrap(); + t.into() +} \ No newline at end of file diff --git a/battlepass/src/tests.rs b/battlepass/src/tests.rs new file mode 100644 index 000000000..b20d2ac33 --- /dev/null +++ b/battlepass/src/tests.rs @@ -0,0 +1,1493 @@ +#![cfg(test)] + +use frame_support::{assert_noop, assert_ok}; +use frame_support::traits::tokens::nonfungibles_v2::{Inspect, InspectEnumerable, Transfer}; +use sp_core::H256; + +use crate::mock::{ + new_test_ext, RuntimeOrigin as Origin, Test, + Battlepass, Control, Nfts, + ALICE, BOB, EVA, TOM, BOT, PROTOCOL_TOKEN_ID, PAYMENT_TOKEN_ID, DOLLARS, + AccountId, StringLimit, +}; +use gamedao_control::types::{AccessModel, FeeModel, OrgType, Org}; + +use super::*; + +fn create_org() -> H256 { + let bounded_str = BoundedVec::truncate_from(vec![1,2]); + let index = Control::org_count(); + let now = frame_system::Pallet::::block_number(); + let org = Org { + index, creator: ALICE, prime: ALICE, name: bounded_str.clone(), cid: bounded_str.clone(), + org_type: OrgType::Individual, fee_model: FeeModel::NoFees, membership_fee: Some(1 * DOLLARS), + gov_currency: PROTOCOL_TOKEN_ID, pay_currency: PAYMENT_TOKEN_ID, access_model: AccessModel::Open, + member_limit: ::MaxMembers::get(), created: now.clone(), mutated: now + }; + let org_id = ::Hashing::hash_of(&org); + + assert_ok!( + Control::create_org( + Origin::signed(ALICE), org.name, org.cid, org.org_type, org.access_model, + org.fee_model, None, org.membership_fee, None, None, None + )); + + org_id +} + +fn get_battlepass_hash(creator: AccountId, org_id: H256, season: u32, price: u16, collection_id: u32) -> H256 { + let battlepass = types::Battlepass { + creator, + org_id, + name: string(), + cid: string(), + season, + price, + collection_id + }; + + ::Hashing::hash_of(&battlepass) +} + +fn create_battlepass(org_id: H256) -> H256 { + let creator = ALICE; + let season = Battlepass::get_battlepass_info(&org_id).0 + 1; + let price = 10; + let collection_id = NextCollectionId::::get().unwrap_or(::CollectionId::initial_value()); + + assert_ok!( + Battlepass::create_battlepass(Origin::signed(creator), org_id, string(), string(), price) + ); + + get_battlepass_hash(creator, org_id, season, price, collection_id) +} + +fn get_reward_hash(battlepass_id: H256, level: u8, transferable: bool, collection_id: u32) -> H256 { + let reward = Reward{ + battlepass_id, + name: string(), + cid: string(), + level, + transferable, + collection_id + }; + + ::Hashing::hash_of(&reward) +} + +fn create_reward(battlepass_id: H256) -> H256 { + let creator = ALICE; + let level = 1; + let max = 1; + let transferable = true; + let collection_id = (Rewards::::iter().count() + 1) as u32; + + assert_ok!( + Battlepass::create_reward(Origin::signed(creator), battlepass_id, string(), string(), Some(max), level, transferable) + ); + + get_reward_hash(battlepass_id, level, transferable, collection_id) +} + +fn add_member(org_id: H256, account: AccountId) { + assert_ok!( + Control::add_member(Origin::signed(account), org_id, account) + ); +} + +fn string() -> BoundedVec{ + BoundedVec::truncate_from(b"string".to_vec()) +} + +#[test] +fn should_be_prime() { + new_test_ext().execute_with(|| { + let org_id = create_org(); + let prime = Battlepass::is_prime(&org_id, ALICE); + assert_eq!(prime.is_ok(), true); + assert_eq!(prime.unwrap(), true); + }) +} + +#[test] +fn create_battlepass_test(){ + new_test_ext().execute_with(|| { + let org_id = create_org(); + let wrong_org_id = ::Hashing::hash_of(&"123"); + let creator = ALICE; + let not_creator = BOB; + let not_member = EVA; + let battlepass_id_1 = get_battlepass_hash(creator, org_id, 1, 10, 0); + let battlepass_id_2 = get_battlepass_hash(creator, org_id, 2, 10, 1); + let battlepass_id_3 = get_battlepass_hash(creator, org_id, 3, 10, 2); + + // Should not create for non existing Org + assert_noop!( + Battlepass::create_battlepass(Origin::signed(creator), wrong_org_id, string(), string(), 10), + Error::::OrgUnknownOrInactive + ); + + // Should not create for inactive Org + assert_ok!( + Control::disable_org(Origin::signed(creator), org_id) + ); + assert_noop!( + Battlepass::create_battlepass(Origin::signed(creator), org_id, string(), string(), 10), + Error::::OrgUnknownOrInactive + ); + assert_ok!( + Control::enable_org(Origin::signed(creator), org_id) + ); + + // Should not create if origin is not a Prime + assert_ok!( + Control::add_member(Origin::signed(not_creator), org_id, not_creator) + ); + assert_noop!( + Battlepass::create_battlepass(Origin::signed(not_creator), org_id, string(), string(), 10), + Error::::AuthorizationError + ); + assert_noop!( + Battlepass::create_battlepass(Origin::signed(not_member), org_id, string(), string(), 10), + Error::::AuthorizationError + ); + + // Should create new Battlepass + assert_ok!( + Battlepass::create_battlepass(Origin::signed(creator), org_id, string(), string(), 10) + ); + // Check if NFT collection created + assert_eq!(>::collections().any(|x| x == 0), true); + // Check if Battlepass created + let battlepass = Battlepasses::::get(battlepass_id_1); + assert_eq!(battlepass.is_some(), true); + assert_eq!(battlepass.unwrap().season, 1); + assert_eq!(Battlepasses::::contains_key(battlepass_id_1), true); + // Check if BattlepassState is DRAFT + assert_eq!(Battlepass::get_battlepass_state(battlepass_id_1), Some(types::BattlepassState::DRAFT)); + // Check if BattlepassInfo created (count = 1, active = None) + let bp_info = BattlepassInfoByOrg::::get(org_id); + assert_eq!(bp_info.is_some(), true); + assert_eq!(bp_info.clone().unwrap().count, 1); + assert_eq!(bp_info.clone().unwrap().active, None); + + // Should create another Battlepass (may be multiple in DRAFT state) + assert_ok!( + Battlepass::create_battlepass(Origin::signed(creator), org_id, string(), string(), 10) + ); + // Check if NFT collection created + assert_eq!(>::collections().any(|x| x == 1), true); + // Check if Battlepass created + let battlepass = Battlepasses::::get(battlepass_id_2); + assert_eq!(battlepass.is_some(), true); + assert_eq!(battlepass.unwrap().season, 2); + assert_eq!(Battlepasses::::contains_key(battlepass_id_2), true); + // Check if BattlepassState is DRAFT + assert_eq!(Battlepass::get_battlepass_state(battlepass_id_2), Some(types::BattlepassState::DRAFT)); + // Check if BattlepassInfo created (count = 2, active = None) + let bp_info = BattlepassInfoByOrg::::get(org_id); + assert_eq!(bp_info.is_some(), true); + assert_eq!(bp_info.clone().unwrap().count, 2); + assert_eq!(bp_info.clone().unwrap().active, None); + + // Should create another Battlepass (even if there is an ACTIVE one) + assert_ok!( + Battlepass::activate_battlepass(Origin::signed(creator), battlepass_id_1) + ); + assert_ok!( + Battlepass::create_battlepass(Origin::signed(creator), org_id, string(), string(), 10) + ); + // Check if NFT collection created + assert_eq!(>::collections().any(|x| x == 1), true); + // Check if Battlepass created + let battlepass = Battlepasses::::get(battlepass_id_3); + assert_eq!(battlepass.is_some(), true); + assert_eq!(battlepass.unwrap().season, 3); + assert_eq!(Battlepasses::::contains_key(battlepass_id_3), true); + // Check if BattlepassState is DRAFT + assert_eq!(Battlepass::get_battlepass_state(battlepass_id_3), Some(types::BattlepassState::DRAFT)); + // Check if BattlepassInfo created (count = 3, active = battlepass_id_1) + let bp_info = BattlepassInfoByOrg::::get(org_id); + assert_eq!(bp_info.is_some(), true); + assert_eq!(bp_info.clone().unwrap().count, 3); + assert_eq!(bp_info.clone().unwrap().active, Some(battlepass_id_1)); + }) +} + +#[test] +fn update_battlepass_test() { + new_test_ext().execute_with(|| { + let org_id = create_org(); + let battlepass_id = create_battlepass(org_id); + let wrong_battlepass_id = ::Hashing::hash_of(&"123"); + let creator = ALICE; + let not_creator = BOB; + let not_member = EVA; + let new_name = BoundedVec::truncate_from(b"new name".to_vec()); + let new_cid = BoundedVec::truncate_from(b"new cid".to_vec()); + let new_price = 20; + + // Should not update unknown Battlepass + assert_noop!( + Battlepass::update_battlepass(Origin::signed(creator), wrong_battlepass_id, Some(string()), Some(string()), Some(10)), + Error::::BattlepassUnknown + ); + + // Should not update if no arguments provided + assert_noop!( + Battlepass::update_battlepass(Origin::signed(creator), battlepass_id, None, None, None), + Error::::NoChangesProvided + ); + + // Should not update if values are the same + assert_noop!( + Battlepass::update_battlepass(Origin::signed(creator), battlepass_id, Some(string()), Some(string()), Some(10)), + Error::::NoChangesProvided + ); + + // Should not update if Org is inactive + assert_ok!( + Control::disable_org(Origin::signed(creator), org_id) + ); + assert_noop!( + Battlepass::update_battlepass(Origin::signed(creator), battlepass_id, Some(new_name.clone()), Some(new_cid.clone()), Some(new_price.clone())), + Error::::OrgUnknownOrInactive + ); + assert_ok!( + Control::enable_org(Origin::signed(creator), org_id) + ); + + // Should not update if origin is not a Prime + assert_ok!( + Control::add_member(Origin::signed(not_creator), org_id, not_creator) + ); + assert_noop!( + Battlepass::update_battlepass(Origin::signed(not_creator), battlepass_id, Some(new_name.clone()), Some(new_cid.clone()), Some(new_price.clone())), + Error::::AuthorizationError + ); + assert_noop!( + Battlepass::update_battlepass(Origin::signed(not_member), battlepass_id, Some(new_name.clone()), Some(new_cid.clone()), Some(new_price.clone())), + Error::::AuthorizationError + ); + + // Should update battlepass + assert_ok!( + Battlepass::update_battlepass(Origin::signed(creator), battlepass_id, Some(new_name.clone()), Some(new_cid.clone()), Some(new_price.clone())), + ); + // Check if Battlepass updated + let updated = Battlepass::get_battlepass(battlepass_id).unwrap(); + assert_eq!(updated.name, new_name.clone()); + assert_eq!(updated.cid, new_cid.clone()); + assert_eq!(updated.price, new_price.clone()); + // Check if Collection metadata updated + assert_eq!(>::collection_attribute(&0, &[]), Some(new_cid.clone().into())); + + // Should update some fields in battlepass + assert_ok!( + Battlepass::update_battlepass(Origin::signed(creator), battlepass_id, None, None, Some(100)), + ); + // Check if Battlepass updated + let updated = Battlepass::get_battlepass(battlepass_id).unwrap(); + assert_eq!(updated.name, new_name.clone()); + assert_eq!(updated.cid, new_cid.clone()); + assert_eq!(updated.price, 100); + + // Should not update if Bot is not added + assert_noop!( + Battlepass::update_battlepass(Origin::signed(BOT), battlepass_id, None, None, Some(200)), + Error::::AuthorizationError + ); + + // Should update Reward by Bot + assert_ok!( + Battlepass::add_bot(Origin::signed(creator), battlepass_id, BOT) + ); + assert_ok!( + Battlepass::update_battlepass(Origin::signed(BOT), battlepass_id, None, None, Some(200)), + ); + // Check if Battlepass updated + let updated = Battlepass::get_battlepass(battlepass_id).unwrap(); + assert_eq!(updated.name, new_name.clone()); + assert_eq!(updated.cid, new_cid.clone()); + assert_eq!(updated.price, 200); + + // Should not update if Battlepass state is ENDED + assert_ok!( + Battlepass::activate_battlepass(Origin::signed(creator), battlepass_id) + ); + assert_ok!( + Battlepass::conclude_battlepass(Origin::signed(creator), battlepass_id) + ); + assert_noop!( + Battlepass::update_battlepass(Origin::signed(creator), battlepass_id, Some(new_name), Some(new_cid), Some(30)), + Error::::BattlepassStateWrong + ); + }) +} + +#[test] +fn activate_battlepass_test() { + new_test_ext().execute_with(|| { + let org_id = create_org(); + let battlepass_id = create_battlepass(org_id); + let battlepass_id_2 = create_battlepass(org_id); + let wrong_battlepass_id = ::Hashing::hash_of(&"123"); + let creator = ALICE; + let not_creator = BOB; + let not_member = EVA; + + // Should not activate unknown Battlepass + assert_noop!( + Battlepass::activate_battlepass(Origin::signed(creator), wrong_battlepass_id), + Error::::BattlepassUnknown + ); + + // Should not activate if Org is inactive + assert_ok!( + Control::disable_org(Origin::signed(creator), org_id) + ); + assert_noop!( + Battlepass::activate_battlepass(Origin::signed(creator), battlepass_id), + Error::::OrgUnknownOrInactive + ); + assert_ok!( + Control::enable_org(Origin::signed(creator), org_id) + ); + + // Should not activate if origin is not a Prime + assert_ok!( + Control::add_member(Origin::signed(not_creator), org_id, not_creator) + ); + assert_noop!( + Battlepass::activate_battlepass(Origin::signed(not_creator), battlepass_id), + Error::::AuthorizationError + ); + assert_noop!( + Battlepass::activate_battlepass(Origin::signed(not_member), battlepass_id), + Error::::AuthorizationError + ); + + // Should activate battlepass + assert_ok!( + Battlepass::activate_battlepass(Origin::signed(creator), battlepass_id) + ); + // Check if BattlepassState is ACTIVE + assert_eq!(Battlepass::get_battlepass_state(battlepass_id), Some(types::BattlepassState::ACTIVE)); + // Check if BattlepassInfo changed (count = 1, active = battlepass_id) + let bp_info = BattlepassInfoByOrg::::get(org_id); + assert_eq!(bp_info.is_some(), true); + assert_eq!(bp_info.clone().unwrap().count, 2); + assert_eq!(bp_info.clone().unwrap().active, Some(battlepass_id)); + + + // Should not activate already active Battlepass + assert_noop!( + Battlepass::activate_battlepass(Origin::signed(creator), battlepass_id), + Error::::BattlepassStateWrong + ); + + // Should not activate if Org has an active battlepass + assert_noop!( + Battlepass::activate_battlepass(Origin::signed(creator), battlepass_id_2), + Error::::BattlepassExists + ); + + }) +} + +#[test] +fn deactivate_battlepass_test() { + new_test_ext().execute_with(|| { + let org_id = create_org(); + let battlepass_id = create_battlepass(org_id); + let wrong_battlepass_id = ::Hashing::hash_of(&"123"); + let creator = ALICE; + let not_creator = BOB; + let not_member = EVA; + + // Should not deactivate unknown Battlepass + assert_noop!( + Battlepass::conclude_battlepass(Origin::signed(creator), wrong_battlepass_id), + Error::::BattlepassUnknown + ); + + // Should not deactivate Battlepass in DRAFT state + assert_noop!( + Battlepass::conclude_battlepass(Origin::signed(creator), battlepass_id), + Error::::BattlepassStateWrong + ); + + // Should not deactivate if origin is not a Prime + assert_ok!( + Battlepass::activate_battlepass(Origin::signed(creator), battlepass_id) + ); + assert_ok!( + Control::add_member(Origin::signed(not_creator), org_id, not_creator) + ); + assert_noop!( + Battlepass::conclude_battlepass(Origin::signed(not_creator), battlepass_id), + Error::::AuthorizationError + ); + assert_noop!( + Battlepass::conclude_battlepass(Origin::signed(not_member), battlepass_id), + Error::::AuthorizationError + ); + + // Should deactivate battlepass + assert_ok!( + Battlepass::conclude_battlepass(Origin::signed(creator), battlepass_id) + ); + // Check if BattlepassState is ENDED + assert_eq!(Battlepass::get_battlepass_state(battlepass_id), Some(types::BattlepassState::ENDED)); + // Check if BattlepassInfo changed (count = 1, active = None) + let bp_info = BattlepassInfoByOrg::::get(org_id); + assert_eq!(bp_info.is_some(), true); + assert_eq!(bp_info.clone().unwrap().count, 1); + assert_eq!(bp_info.clone().unwrap().active, None); + + // Should not activate already ended Battlepass + assert_noop!( + Battlepass::activate_battlepass(Origin::signed(creator), battlepass_id), + Error::::BattlepassStateWrong + ); + + // Should not deactivate already ended Battlepass + assert_noop!( + Battlepass::conclude_battlepass(Origin::signed(creator), battlepass_id), + Error::::BattlepassStateWrong + ); + + }) +} + +#[test] +fn claim_battlepass_test() { + new_test_ext().execute_with(|| { + let org_id = create_org(); + let battlepass_id = create_battlepass(org_id); + let wrong_battlepass_id = ::Hashing::hash_of(&"123"); + let cid = BoundedVec::truncate_from(b"new cid".to_vec()); + let creator = ALICE; + let not_creator = BOB; + let not_creator_2 = TOM; + let not_creator_3 = 22u64; + let not_member = EVA; + let not_member_2 = 40; + add_member(org_id, not_creator); + add_member(org_id, not_creator_2); + add_member(org_id, not_creator_3); + + // Should not claim unknown Battlepass + assert_noop!( + Battlepass::claim_battlepass(Origin::signed(creator), wrong_battlepass_id, creator, None), + Error::::BattlepassUnknown + ); + + // Should not claim Battlepass in DRAFT state + assert_noop!( + Battlepass::claim_battlepass(Origin::signed(creator), battlepass_id, creator, None), + Error::::BattlepassStateWrong + ); + + // Should not claim if Org is inactive + assert_ok!( + Battlepass::activate_battlepass(Origin::signed(creator), battlepass_id) + ); + assert_ok!( + Control::disable_org(Origin::signed(creator), org_id) + ); + assert_noop!( + Battlepass::claim_battlepass(Origin::signed(creator), battlepass_id, creator, None), + Error::::OrgUnknownOrInactive + ); + assert_ok!( + Control::enable_org(Origin::signed(creator), org_id) + ); + + // Should not claim by Bot if Bot account was not added + assert_noop!( + Battlepass::claim_battlepass(Origin::signed(BOT), battlepass_id, creator, None), + Error::::AuthorizationError + ); + + // Should not claim for others if origin is not a Prime + assert_noop!( + Battlepass::claim_battlepass(Origin::signed(not_creator), battlepass_id, creator, None), + Error::::AuthorizationError + ); + assert_noop!( + Battlepass::claim_battlepass(Origin::signed(not_member), battlepass_id, creator, None), + Error::::AuthorizationError + ); + // Should not claim for self + assert_noop!( + Battlepass::claim_battlepass(Origin::signed(not_creator), battlepass_id, not_creator, None), + Error::::AuthorizationError + ); + + // Should claim for others by Prime + assert_ok!( + Battlepass::claim_battlepass(Origin::signed(creator), battlepass_id, not_creator, None) + ); + // Check if NFT minted + assert_eq!(>::items(&0).any(|x| x == 0) , true); + // Check NFT metadata + assert_eq!(>::attribute(&0, &0, &[]), Some(string().into())); + + // Should not claim if it was already claimed + assert_noop!( + Battlepass::claim_battlepass(Origin::signed(creator), battlepass_id, not_creator, None), + Error::::BattlepassOwnershipExists + ); + + // Should claim again after transferring Battlepass NFT to someone else + assert_ok!( + >::transfer(&0, &0, ¬_member_2) + ); + assert_ok!( + Battlepass::claim_battlepass(Origin::signed(creator), battlepass_id, not_creator, None) + ); + // Check if NFT minted + assert_eq!(>::items(&0).any(|x| x == 1) , true); + + // Should not claim if user received Battlepass NFT from someone else + assert_noop!( + Battlepass::claim_battlepass(Origin::signed(creator), battlepass_id, not_member_2, None), + Error::::BattlepassOwnershipExists + ); + + // Should claim for others by Bot + assert_ok!( + Battlepass::add_bot(Origin::signed(creator), battlepass_id, BOT) + ); + assert_ok!( + Battlepass::claim_battlepass(Origin::signed(BOT), battlepass_id, not_creator_3, None) + ); + // Check if NFT minted + assert_eq!(>::items(&0).any(|x| x == 2) , true); + + // Should claim for accounts outside of org + assert_ok!( + Battlepass::claim_battlepass(Origin::signed(creator), battlepass_id, not_member, Some(cid.clone())), + ); + // Check if NFT minted + assert_eq!(>::items(&0).any(|x| x == 3) , true); + // Check NFT metadata + assert_eq!(>::attribute(&0, &3, &[]), Some(cid.into())); + + // Should not claim Battlepass in ENDED state + assert_ok!( + Battlepass::conclude_battlepass(Origin::signed(creator), battlepass_id) + ); + assert_noop!( + Battlepass::claim_battlepass(Origin::signed(creator), battlepass_id, creator, None), + Error::::BattlepassStateWrong + ); + + }) +} + +#[test] +fn set_points_test() { + new_test_ext().execute_with(|| { + let org_id = create_org(); + let battlepass_id = create_battlepass(org_id); + let wrong_battlepass_id = ::Hashing::hash_of(&"123"); + let creator = ALICE; + let not_creator = BOB; + let not_member = EVA; + + // Should not set if Battlepass unknown + assert_noop!( + Battlepass::set_points(Origin::signed(creator), wrong_battlepass_id, creator, 10), + Error::::BattlepassUnknown + ); + + // Should not set if Battlepass in DRAFT state + assert_noop!( + Battlepass::set_points(Origin::signed(creator), battlepass_id, creator, 10), + Error::::BattlepassStateWrong + ); + + // Should not set if Org is inactive + assert_ok!( + Battlepass::activate_battlepass(Origin::signed(creator), battlepass_id) + ); + assert_ok!( + Control::disable_org(Origin::signed(creator), org_id) + ); + assert_noop!( + Battlepass::set_points(Origin::signed(creator), battlepass_id, creator, 10), + Error::::OrgUnknownOrInactive + ); + assert_ok!( + Control::enable_org(Origin::signed(creator), org_id) + ); + + // Should not set if origin is not a Prime + assert_ok!( + Control::add_member(Origin::signed(not_creator), org_id, not_creator) + ); + assert_noop!( + Battlepass::set_points(Origin::signed(not_creator), battlepass_id, not_creator, 10), + Error::::AuthorizationError + ); + assert_noop!( + Battlepass::set_points(Origin::signed(not_member), battlepass_id, creator, 10), + Error::::AuthorizationError + ); + + // Should not set by Bot if Bot is not authorized + assert_noop!( + Battlepass::set_points(Origin::signed(BOT), battlepass_id, creator, 10), + Error::::AuthorizationError + ); + + // Should not set if user does not have access to Battlepass + assert_noop!( + Battlepass::set_points(Origin::signed(creator), battlepass_id, not_member, 10), + Error::::BattlepassOwnershipDoesntExist + ); + + // Should set points by Prime + assert_ok!( + Battlepass::claim_battlepass(Origin::signed(creator), battlepass_id, not_creator, None) + ); + assert_ok!( + Battlepass::set_points(Origin::signed(creator), battlepass_id, not_creator, 10) + ); + // Check if Points record updated + assert_eq!(Points::::get(battlepass_id, not_creator) == 10, true); + + // Should set points by Bot + assert_ok!( + Battlepass::add_bot(Origin::signed(creator), battlepass_id, BOT) + ); + assert_ok!( + Battlepass::set_points(Origin::signed(BOT), battlepass_id, not_creator, 20) + ); + // Check if Points record updated + assert_eq!(Points::::get(battlepass_id, not_creator) == 20, true); + + // Should not set if Battlepass in ENDED state + assert_ok!( + Battlepass::conclude_battlepass(Origin::signed(creator), battlepass_id) + ); + assert_noop!( + Battlepass::set_points(Origin::signed(creator), battlepass_id, creator, 10), + Error::::BattlepassStateWrong + ); + + }) +} + +#[test] +fn create_reward_test() { + new_test_ext().execute_with(|| { + let org_id = create_org(); + let battlepass_id = create_battlepass(org_id); + let wrong_battlepass_id = ::Hashing::hash_of(&"123"); + let creator = ALICE; + let not_creator = BOB; + let not_member = EVA; + + // Should not create if Battlepass unknown + assert_noop!( + Battlepass::create_reward(Origin::signed(creator), wrong_battlepass_id, string(), string(), Some(1), 1, true), + Error::::BattlepassUnknown + ); + + // Should not create if Org is inactive + assert_ok!( + Control::disable_org(Origin::signed(creator), org_id) + ); + assert_noop!( + Battlepass::create_reward(Origin::signed(creator), battlepass_id, string(), string(), Some(1), 1, true), + Error::::OrgUnknownOrInactive + ); + assert_ok!( + Control::enable_org(Origin::signed(creator), org_id) + ); + + // Should not create if origin is not a Prime + assert_ok!( + Control::add_member(Origin::signed(not_creator), org_id, not_creator) + ); + assert_noop!( + Battlepass::create_reward(Origin::signed(not_creator), battlepass_id, string(), string(), Some(1), 1, true), + Error::::AuthorizationError + ); + assert_noop!( + Battlepass::create_reward(Origin::signed(not_member), battlepass_id, string(), string(), Some(1), 1, true), + Error::::AuthorizationError + ); + + // Should create Reward if Battlepass state is DRAFT + assert_ok!( + Battlepass::create_reward(Origin::signed(creator), battlepass_id, string(), string(), Some(1), 1, true) + ); + // Check if NFT collection created + assert_eq!(>::collections().any(|x| x == 1) , true); + // Check if collection owner is a Prime + assert_eq!(>::collection_owner(&1).unwrap(), creator); + // Check if Reward created + let reward_id = get_reward_hash(battlepass_id, 1, true, 1); + let reward = Rewards::::get(reward_id); + assert_eq!(reward.is_some(), true); + assert_eq!(reward.unwrap().collection_id, 1); + // Check if RewardState is ACTIVE + assert_eq!(RewardStates::::get(reward_id), Some(RewardState::ACTIVE)); + + // Should create Reward if Battlepass state is ACTIVE + assert_ok!( + Battlepass::activate_battlepass(Origin::signed(creator), battlepass_id) + ); + assert_ok!( + Battlepass::create_reward(Origin::signed(creator), battlepass_id, string(), string(), Some(1), 1, true) + ); + // Check if NFT collection created + assert_eq!(>::collections().any(|x| x == 2) , true); + // Check if collection owner is a Prime + assert_eq!(>::collection_owner(&2).unwrap(), creator); + // Check if Reward created + let reward_id = get_reward_hash(battlepass_id, 1, true, 2); + let reward = Rewards::::get(reward_id); + assert_eq!(reward.is_some(), true); + assert_eq!(reward.unwrap().collection_id, 2); + // Check if RewardState is ACTIVE + assert_eq!(RewardStates::::get(reward_id), Some(RewardState::ACTIVE)); + + // Should not create if Bot is not added + assert_noop!( + Battlepass::create_reward(Origin::signed(BOT), battlepass_id, string(), string(), Some(1), 1, true), + Error::::AuthorizationError + ); + + // Should create Reward by Bot + assert_ok!( + Battlepass::add_bot(Origin::signed(creator), battlepass_id, BOT) + ); + assert_ok!( + Battlepass::create_reward(Origin::signed(BOT), battlepass_id, string(), string(), Some(1), 1, true) + ); + // Check if NFT collection created + assert_eq!(>::collections().any(|x| x == 3) , true); + // Check if collection owner is a Prime + assert_eq!(>::collection_owner(&3).unwrap(), creator); + // Check if Reward created + let reward_id = get_reward_hash(battlepass_id, 1, true, 3); + let reward = Rewards::::get(reward_id); + assert_eq!(reward.is_some(), true); + assert_eq!(reward.unwrap().collection_id, 3); + // Check if RewardState is ACTIVE + assert_eq!(RewardStates::::get(reward_id), Some(RewardState::ACTIVE)); + + + // Should not create Reward if Battlepass state is ENDED + assert_ok!( + Battlepass::conclude_battlepass(Origin::signed(creator), battlepass_id) + ); + assert_noop!( + Battlepass::create_reward(Origin::signed(creator), battlepass_id, string(), string(), Some(1), 1, true), + Error::::BattlepassStateWrong + ); + + }) +} + +#[test] +fn update_reward_test() { + new_test_ext().execute_with(|| { + let org_id = create_org(); + let battlepass_id = create_battlepass(org_id); + let reward_id = create_reward(battlepass_id); + let reward_id_2 = create_reward(battlepass_id); + let wrong_id = ::Hashing::hash_of(&"123"); + let creator = ALICE; + let not_creator = BOB; + let not_member = EVA; + let new_name = BoundedVec::truncate_from(b"new name".to_vec()); + let new_cid = BoundedVec::truncate_from(b"new cid".to_vec()); + let new_transferable = false; + + // Should not update if Reward unknown + assert_noop!( + Battlepass::update_reward(Origin::signed(creator), wrong_id, Some(new_name.clone()), Some(new_cid.clone()), Some(new_transferable.clone())), + Error::::RewardUnknown + ); + + // Should not update if no arguments provided + assert_noop!( + Battlepass::update_reward(Origin::signed(creator), reward_id, None, None, None), + Error::::NoChangesProvided + ); + + // Should not update if values are the same + assert_noop!( + Battlepass::update_reward(Origin::signed(creator), reward_id, Some(string()), Some(string()), Some(true)), + Error::::NoChangesProvided + ); + + // Should not update if Battlepass unknown + Rewards::::mutate(reward_id_2, |reward| { + if let Some(r) = reward { + r.battlepass_id = wrong_id; + } + } ); + assert_noop!( + Battlepass::update_reward(Origin::signed(creator), reward_id_2, Some(new_name.clone()), Some(new_cid.clone()), Some(new_transferable.clone())), + Error::::BattlepassUnknown + ); + + // Should not update if Org is inactive + assert_ok!( + Control::disable_org(Origin::signed(creator), org_id) + ); + assert_noop!( + Battlepass::update_reward(Origin::signed(creator), reward_id, Some(new_name.clone()), Some(new_cid.clone()), Some(new_transferable.clone())), + Error::::OrgUnknownOrInactive + ); + assert_ok!( + Control::enable_org(Origin::signed(creator), org_id) + ); + + // Should not update if origin is not a Prime + assert_ok!( + Control::add_member(Origin::signed(not_creator), org_id, not_creator) + ); + assert_noop!( + Battlepass::update_reward(Origin::signed(not_creator), reward_id, Some(new_name.clone()), Some(new_cid.clone()), Some(new_transferable.clone())), + Error::::AuthorizationError + ); + assert_noop!( + Battlepass::update_reward(Origin::signed(not_member), reward_id, Some(new_name.clone()), Some(new_cid.clone()), Some(new_transferable.clone())), + Error::::AuthorizationError + ); + + // Should update Reward + assert_ok!( + Battlepass::update_reward(Origin::signed(creator), reward_id, Some(new_name.clone()), Some(new_cid.clone()), Some(new_transferable.clone())) + ); + // Check if Reward updated + let updated = Battlepass::get_reward(reward_id).unwrap(); + assert_eq!(updated.name, new_name.clone()); + assert_eq!(updated.cid, new_cid.clone()); + assert_eq!(updated.transferable, new_transferable.clone()); + // Check if Collection metadata updated + assert_eq!(>::collection_attribute(&1, &[]), Some(new_cid.clone().into())); + + // Should update some fields in Reward + assert_ok!( + Battlepass::update_reward(Origin::signed(creator), reward_id, None, Some(new_name.clone()), None) + ); + // Check if Reward updated + let updated = Battlepass::get_reward(reward_id).unwrap(); + assert_eq!(updated.name, new_name.clone()); + assert_eq!(updated.cid, new_name.clone()); + assert_eq!(updated.transferable, new_transferable.clone()); + + // Should not update if Bot is not added + assert_noop!( + Battlepass::update_reward(Origin::signed(BOT), reward_id, Some(new_name.clone()), Some(new_cid.clone()), Some(true)), + Error::::AuthorizationError + ); + + // Should update Reward by Bot + assert_ok!( + Battlepass::add_bot(Origin::signed(creator), battlepass_id, BOT) + ); + assert_ok!( + Battlepass::update_reward(Origin::signed(BOT), reward_id, Some(new_name.clone()), Some(new_cid.clone()), Some(true)) + ); + // Check if Reward updated + let updated = Battlepass::get_reward(reward_id).unwrap(); + assert_eq!(updated.name, new_name.clone()); + assert_eq!(updated.cid, new_cid.clone()); + assert_eq!(updated.transferable, true); + + // Should not update if Battlepass state is ENDED + assert_ok!( + Battlepass::activate_battlepass(Origin::signed(creator), battlepass_id) + ); + assert_ok!( + Battlepass::conclude_battlepass(Origin::signed(creator), battlepass_id) + ); + assert_noop!( + Battlepass::update_reward(Origin::signed(creator), reward_id, Some(new_name.clone()), Some(new_cid.clone()), Some(false)), + Error::::BattlepassStateWrong + ); + + // Should not update if Reward inactive + assert_ok!( + Battlepass::disable_reward(Origin::signed(creator), reward_id) + ); + assert_noop!( + Battlepass::update_reward(Origin::signed(creator), reward_id, Some(new_name.clone()), Some(new_cid.clone()), Some(false)), + Error::::RewardInactive + ); + + }) +} + +#[test] +fn disable_reward_test() { + new_test_ext().execute_with(|| { + let org_id = create_org(); + let battlepass_id = create_battlepass(org_id); + let reward_id = create_reward(battlepass_id); + let reward_id_2 = create_reward(battlepass_id); + let reward_id_3 = create_reward(battlepass_id); + let wrong_id = ::Hashing::hash_of(&"123"); + let creator = ALICE; + let not_creator = BOB; + let not_member = EVA; + + // Should not disable if Reward unknown + assert_noop!( + Battlepass::disable_reward(Origin::signed(creator), wrong_id), + Error::::RewardUnknown + ); + + // Should not disable if Battlepass unknown + Rewards::::mutate(reward_id_2, |reward| { + if let Some(r) = reward { + r.battlepass_id = wrong_id; + } + } ); + assert_noop!( + Battlepass::disable_reward(Origin::signed(creator), reward_id_2), + Error::::BattlepassUnknown + ); + + // Should not disable if origin is not a Prime + assert_ok!( + Control::add_member(Origin::signed(not_creator), org_id, not_creator) + ); + assert_noop!( + Battlepass::disable_reward(Origin::signed(not_creator), reward_id), + Error::::AuthorizationError + ); + assert_noop!( + Battlepass::disable_reward(Origin::signed(not_member), reward_id), + Error::::AuthorizationError + ); + + // Should disable Reward + assert_ok!( + Battlepass::disable_reward(Origin::signed(creator), reward_id) + ); + // Check if RewardState is INACTIVE + assert_eq!(RewardStates::::get(reward_id), Some(RewardState::INACTIVE)); + + // Should not disable if Reward inactive + assert_noop!( + Battlepass::disable_reward(Origin::signed(creator), reward_id), + Error::::RewardInactive + ); + + // Should not disable if Bot is not added + assert_noop!( + Battlepass::disable_reward(Origin::signed(BOT), reward_id_3), + Error::::AuthorizationError + ); + + // Should disable Reward by Bot + assert_ok!( + Battlepass::add_bot(Origin::signed(creator), battlepass_id, BOT) + ); + assert_ok!( + Battlepass::disable_reward(Origin::signed(BOT), reward_id_3) + ); + // Check if RewardState is INACTIVE + assert_eq!(RewardStates::::get(reward_id_3), Some(RewardState::INACTIVE)); + + }) +} + +#[test] +fn claim_reward_test() { + new_test_ext().execute_with(|| { + let org_id = create_org(); + let battlepass_id = create_battlepass(org_id); + let wrong_id = ::Hashing::hash_of(&"123"); + let cid = BoundedVec::truncate_from(b"new cid".to_vec()); + let creator = ALICE; + let not_creator = BOB; + let not_creator_2 = TOM; + let not_creator_3 = 30; + let not_creator_4 = 31; + let not_member = EVA; + let not_member_2 = 40; + add_member(org_id, not_creator); + add_member(org_id, not_creator_2); + add_member(org_id, not_creator_3); + add_member(org_id, not_creator_4); + + // Should not claim if Reward unknown + assert_noop!( + Battlepass::claim_reward(Origin::signed(creator), wrong_id, creator, None), + Error::::RewardUnknown + ); + + // Should not claim if Reward is INACTIVE + let reward_id = create_reward(battlepass_id); + assert_ok!( + Battlepass::disable_reward(Origin::signed(creator), reward_id) + ); + assert_noop!( + Battlepass::claim_reward(Origin::signed(creator), reward_id, creator, None), + Error::::RewardInactive + ); + + // Should not disable if Battlepass unknown + let reward_id = create_reward(battlepass_id); + Rewards::::mutate(reward_id, |reward| { + if let Some(r) = reward { + r.battlepass_id = wrong_id; + } + } ); + assert_noop!( + Battlepass::claim_reward(Origin::signed(creator), reward_id, creator, None), + Error::::BattlepassUnknown + ); + + // Should not claim if Battlepass state is DRAFT + let reward_id = create_reward(battlepass_id); + assert_noop!( + Battlepass::claim_reward(Origin::signed(creator), reward_id, creator, None), + Error::::BattlepassStateWrong + ); + + // Should not claim if Org is inactive + assert_ok!( + Battlepass::activate_battlepass(Origin::signed(creator), battlepass_id) + ); + assert_ok!( + Control::disable_org(Origin::signed(creator), org_id) + ); + assert_noop!( + Battlepass::claim_reward(Origin::signed(creator), reward_id, creator, None), + Error::::OrgUnknownOrInactive + ); + assert_ok!( + Control::enable_org(Origin::signed(creator), org_id) + ); + + // Should not claim by Bot if Bot account was not added + assert_noop!( + Battlepass::claim_reward(Origin::signed(BOT), reward_id, creator, None), + Error::::AuthorizationError + ); + + // Should not claim for others if origin is not a Prime or Bot + assert_noop!( + Battlepass::claim_reward(Origin::signed(not_creator), reward_id, creator, None), + Error::::AuthorizationError + ); + assert_noop!( + Battlepass::claim_reward(Origin::signed(not_member), reward_id, creator, None), + Error::::AuthorizationError + ); + // Should not claim for self + assert_noop!( + Battlepass::claim_reward(Origin::signed(not_member), reward_id, not_member, None), + Error::::AuthorizationError + ); + + // Should not claim Reward if user didn't claim Battlepass + assert_noop!( + Battlepass::claim_reward(Origin::signed(creator), reward_id, not_creator, None), + Error::::BattlepassOwnershipDoesntExist + ); + + // Should not claim Reward if no NFT for claimed Battlepass + assert_ok!( + Battlepass::claim_battlepass(Origin::signed(creator), battlepass_id, not_creator, None) + ); + assert_ok!( + Nfts::do_burn(0, 0, |_details| { Ok(()) }) + ); + assert_noop!( + Battlepass::claim_reward(Origin::signed(creator), reward_id, not_creator, None), + Error::::BattlepassOwnershipDoesntExist + ); + + // Should not claim Reward if user lost ownership of Battlepass NFT + assert_ok!( + Battlepass::claim_battlepass(Origin::signed(creator), battlepass_id, creator, None) + ); + assert_ok!( + >::transfer(&0, &1, ¬_member_2) + ); + assert_noop!( + Battlepass::claim_reward(Origin::signed(creator), reward_id, creator, None), + Error::::BattlepassOwnershipDoesntExist + ); + + // Should not claim if user's level is too low + assert_ok!( + Battlepass::claim_battlepass(Origin::signed(creator), battlepass_id, not_creator_3, None) + ); + assert_noop!( + Battlepass::claim_reward(Origin::signed(creator), reward_id, not_creator_3, None), + Error::::LevelNotReached + ); + + // Should claim Reward + assert_ok!( + Battlepass::set_points(Origin::signed(creator), battlepass_id, not_creator_3, 10) + ); + assert_ok!( + Battlepass::add_level(Origin::signed(creator), battlepass_id, 1, 10) + ); + assert_ok!( + Battlepass::claim_reward(Origin::signed(creator), reward_id, not_creator_3, None) + ); + // Check if Reward claimed + assert_eq!(ClaimedRewards::::contains_key(reward_id, not_creator_3), true); + // Check if NFT minted + assert_eq!(>::items(&3).any(|x| x == 3) , true); + // Check NFT metadata + assert_eq!(>::attribute(&3, &3, &[]), Some(string().into())); + + // Should not claim if Reward already claimed + assert_noop!( + Battlepass::claim_reward(Origin::signed(creator), reward_id, not_creator_3, None), + Error::::RewardClaimed + ); + + // Should not claim if max limit reached + assert_ok!( + Battlepass::claim_battlepass(Origin::signed(creator), battlepass_id, not_creator_4, None) + ); + assert_ok!( + Battlepass::set_points(Origin::signed(creator), battlepass_id, not_creator_4, 10) + ); + assert_noop!( + Battlepass::claim_reward(Origin::signed(creator), reward_id, not_creator_4, None), + pallet_nfts::Error::::MaxSupplyReached + ); + + // Should claim Reward by Bot + let reward_id = create_reward(battlepass_id); + assert_ok!( + Battlepass::add_bot(Origin::signed(creator), battlepass_id, BOT) + ); + assert_ok!( + Battlepass::claim_reward(Origin::signed(BOT), reward_id, not_creator_4, None) + ); + // Check if Reward claimed + assert_eq!(ClaimedRewards::::contains_key(reward_id, not_creator_4), true); + // Check if NFT minted + assert_eq!(>::items(&4).any(|x| x == 5) , true); + // Check NFT metadata + assert_eq!(>::attribute(&4, &5, &[]), Some(string().into())); + + // Should claim Reward for non-member + let reward_id = create_reward(battlepass_id); + assert_ok!( + Battlepass::claim_battlepass(Origin::signed(creator), battlepass_id, not_member, None) + ); + assert_ok!( + Battlepass::set_points(Origin::signed(creator), battlepass_id, not_member, 10) + ); + assert_ok!( + Battlepass::claim_reward(Origin::signed(creator), reward_id, not_member, Some(cid.clone())) + ); + // Check if Reward claimed + assert_eq!(ClaimedRewards::::contains_key(reward_id, not_member), true); + // Check if NFT minted + assert_eq!(>::items(&5).any(|x| x == 7) , true); + // Check NFT metadata + assert_eq!(>::attribute(&5, &7, &[]), Some(cid.into())); + + // Should claim Reward after receiving Battlepass NFT from elsewhere + let reward_id = create_reward(battlepass_id); + assert_ok!( + Battlepass::set_points(Origin::signed(creator), battlepass_id, not_member_2, 10) + ); + assert_ok!( + Battlepass::claim_reward(Origin::signed(creator), reward_id, not_member_2, None) + ); + // Check if Reward claimed + assert_eq!(ClaimedRewards::::contains_key(reward_id, not_member_2), true); + // Check if NFT minted + assert_eq!(>::items(&6).any(|x| x == 8) , true); + // Check NFT metadata + assert_eq!(>::attribute(&6, &8, &[]), Some(string().into())); + + // Should not claim if Battlepass state is ENDED + assert_ok!( + Battlepass::conclude_battlepass(Origin::signed(creator), battlepass_id) + ); + assert_noop!( + Battlepass::claim_reward(Origin::signed(creator), reward_id, not_creator_3, None), + Error::::BattlepassStateWrong + ); + + }) +} + +#[test] +fn add_level_test() { + new_test_ext().execute_with(|| { + let org_id = create_org(); + let battlepass_id = create_battlepass(org_id); + let wrong_battlepass_id = ::Hashing::hash_of(&"123"); + let creator = ALICE; + let not_creator = BOB; + let not_member = EVA; + add_member(org_id, not_creator); + + // Should not add if Battlepass unknown + assert_noop!( + Battlepass::add_level(Origin::signed(creator), wrong_battlepass_id, 1, 10), + Error::::BattlepassUnknown + ); + + // Should not add if Org is inactive + assert_ok!( + Control::disable_org(Origin::signed(creator), org_id) + ); + assert_noop!( + Battlepass::add_level(Origin::signed(creator), battlepass_id, 1, 10), + Error::::OrgUnknownOrInactive + ); + assert_ok!( + Control::enable_org(Origin::signed(creator), org_id) + ); + + // Should not add if origin is not a Prime + assert_noop!( + Battlepass::add_level(Origin::signed(not_creator), battlepass_id, 1, 10), + Error::::AuthorizationError + ); + assert_noop!( + Battlepass::add_level(Origin::signed(not_member), battlepass_id, 1, 10), + Error::::AuthorizationError + ); + + // Should add Level for DRAFT Battlepass + assert_ok!( + Battlepass::add_level(Origin::signed(creator), battlepass_id, 1, 10) + ); + // Check if Level added + assert_eq!(Levels::::contains_key(battlepass_id, 1), true); + + // Should add Level for ACTIVE Battlepass + assert_ok!( + Battlepass::activate_battlepass(Origin::signed(creator), battlepass_id) + ); + assert_ok!( + Battlepass::add_level(Origin::signed(creator), battlepass_id, 2, 10) + ); + // Check if Level added + assert_eq!(Levels::::contains_key(battlepass_id, 2), true); + + // Should not add Level by Bot if Bot account was not added + assert_noop!( + Battlepass::add_level(Origin::signed(BOT), battlepass_id, 3, 10), + Error::::AuthorizationError + ); + + // Should add Level by Bot + assert_ok!( + Battlepass::add_bot(Origin::signed(creator), battlepass_id, BOT) + ); + assert_ok!( + Battlepass::add_level(Origin::signed(BOT), battlepass_id, 3, 10) + ); + // Check if Level added + assert_eq!(Levels::::contains_key(battlepass_id, 3), true); + + // Should not add if Battlepass in ENDED state + assert_ok!( + Battlepass::conclude_battlepass(Origin::signed(creator), battlepass_id) + ); + assert_noop!( + Battlepass::add_level(Origin::signed(creator), battlepass_id, 1, 10), + Error::::BattlepassStateWrong + ); + + }) +} + +#[test] +fn remove_level_test() { + new_test_ext().execute_with(|| { + let org_id = create_org(); + let battlepass_id = create_battlepass(org_id); + let wrong_battlepass_id = ::Hashing::hash_of(&"123"); + let creator = ALICE; + let not_creator = BOB; + let not_member = EVA; + add_member(org_id, not_creator); + + // Should not remove if Battlepass unknown + assert_noop!( + Battlepass::remove_level(Origin::signed(creator), wrong_battlepass_id, 1), + Error::::BattlepassUnknown + ); + + // Should not remove if Org is inactive + assert_ok!( + Control::disable_org(Origin::signed(creator), org_id) + ); + assert_noop!( + Battlepass::remove_level(Origin::signed(creator), battlepass_id, 1), + Error::::OrgUnknownOrInactive + ); + assert_ok!( + Control::enable_org(Origin::signed(creator), org_id) + ); + + // Should not remove if origin is not a Prime + assert_noop!( + Battlepass::remove_level(Origin::signed(not_creator), battlepass_id, 1), + Error::::AuthorizationError + ); + assert_noop!( + Battlepass::remove_level(Origin::signed(not_member), battlepass_id, 1), + Error::::AuthorizationError + ); + + // Should not remove if no such Level + assert_noop!( + Battlepass::remove_level(Origin::signed(creator), battlepass_id, 1), + Error::::LevelUnknown + ); + + // Should remove Level for DRAFT Battlepass + assert_ok!( + Battlepass::add_level(Origin::signed(creator), battlepass_id, 1, 10) + ); + assert_eq!(Levels::::contains_key(battlepass_id, 1), true); + assert_ok!( + Battlepass::remove_level(Origin::signed(creator), battlepass_id, 1) + ); + // Check if Level removed + assert_eq!(Levels::::contains_key(battlepass_id, 1), false); + + // Should remove Level for ACTIVE Battlepass + assert_ok!( + Battlepass::activate_battlepass(Origin::signed(creator), battlepass_id) + ); + assert_ok!( + Battlepass::add_level(Origin::signed(creator), battlepass_id, 2, 10) + ); + assert_eq!(Levels::::contains_key(battlepass_id, 2), true); + assert_ok!( + Battlepass::remove_level(Origin::signed(creator), battlepass_id, 2) + ); + // Check if Level removed + assert_eq!(Levels::::contains_key(battlepass_id, 2), false); + + + // Should not remove Level by Bot if Bot account was not added + assert_noop!( + Battlepass::remove_level(Origin::signed(BOT), battlepass_id, 3), + Error::::AuthorizationError + ); + + // Should remove Level by Bot + assert_ok!( + Battlepass::add_bot(Origin::signed(creator), battlepass_id, BOT) + ); + assert_ok!( + Battlepass::add_level(Origin::signed(creator), battlepass_id, 3, 10) + ); + assert_eq!(Levels::::contains_key(battlepass_id, 3), true); + assert_ok!( + Battlepass::remove_level(Origin::signed(BOT), battlepass_id, 3) + ); + // Check if Level added + assert_eq!(Levels::::contains_key(battlepass_id, 3), false); + + + // Should not remove if Battlepass in ENDED state + assert_ok!( + Battlepass::conclude_battlepass(Origin::signed(creator), battlepass_id) + ); + assert_noop!( + Battlepass::remove_level(Origin::signed(creator), battlepass_id, 1), + Error::::BattlepassStateWrong + ); + + }) +} + +#[test] +fn add_bot_test() { + new_test_ext().execute_with(|| { + let org_id = create_org(); + let battlepass_id = create_battlepass(org_id); + let wrong_battlepass_id = ::Hashing::hash_of(&"123"); + let creator = ALICE; + let not_creator = BOB; + let not_member = EVA; + add_member(org_id, not_creator); + + // Should not add if Battlepass unknown + assert_noop!( + Battlepass::add_bot(Origin::signed(creator), wrong_battlepass_id, 1), + Error::::BattlepassUnknown + ); + + // Should not add if Org is inactive + assert_ok!( + Control::disable_org(Origin::signed(creator), org_id) + ); + assert_noop!( + Battlepass::add_bot(Origin::signed(creator), battlepass_id, 1), + Error::::OrgUnknownOrInactive + ); + assert_ok!( + Control::enable_org(Origin::signed(creator), org_id) + ); + + // Should not add if origin is not a Prime + assert_noop!( + Battlepass::add_bot(Origin::signed(not_creator), battlepass_id, 1), + Error::::AuthorizationError + ); + assert_noop!( + Battlepass::add_bot(Origin::signed(not_member), battlepass_id, 1), + Error::::AuthorizationError + ); + + // Should add Bot for DRAFT Battlepass + assert_ok!( + Battlepass::add_bot(Origin::signed(creator), battlepass_id, 1) + ); + // Check if bot added + let bp_info = BattlepassInfoByOrg::::get(org_id); + assert_eq!(bp_info.is_some(), true); + assert_eq!(bp_info.clone().unwrap().count, 1); + assert_eq!(bp_info.clone().unwrap().active, None); + assert_eq!(bp_info.clone().unwrap().bot, Some(1)); + + // Should add Bot for ACTIVE Battlepass + assert_ok!( + Battlepass::activate_battlepass(Origin::signed(creator), battlepass_id) + ); + assert_ok!( + Battlepass::add_bot(Origin::signed(creator), battlepass_id, 2) + ); + // Check if bot added + let bp_info = BattlepassInfoByOrg::::get(org_id); + assert_eq!(bp_info.is_some(), true); + assert_eq!(bp_info.clone().unwrap().count, 1); + assert_eq!(bp_info.clone().unwrap().active, Some(battlepass_id)); + assert_eq!(bp_info.clone().unwrap().bot, Some(2)); + + // Should not add if Battlepass in ENDED state + assert_ok!( + Battlepass::conclude_battlepass(Origin::signed(creator), battlepass_id) + ); + assert_noop!( + Battlepass::add_bot(Origin::signed(creator), battlepass_id, 1), + Error::::BattlepassStateWrong + ); + + }) +} diff --git a/battlepass/src/types.rs b/battlepass/src/types.rs new file mode 100644 index 000000000..18789b437 --- /dev/null +++ b/battlepass/src/types.rs @@ -0,0 +1,62 @@ +use frame_support::pallet_prelude::*; +use codec::MaxEncodedLen; + +#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, TypeInfo, MaxEncodedLen)] +pub enum BattlepassState { + DRAFT, + ACTIVE, + ENDED, +} +impl Default for BattlepassState { + fn default() -> Self { + Self::DRAFT + } +} + +#[derive(Encode, Decode, Default, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +/// Battlepass struct +/// +/// `collection_id`: Collection that will store all claimed Battlepass-NFTs +pub struct Battlepass { + pub creator: AccountId, + pub org_id: Hash, + pub name: BoundedString, + pub cid: BoundedString, + pub season: u32, + pub price: u16, // TODO: introduce currency + pub collection_id: CollectionId +} + +#[derive(Encode, Decode, Default, Clone, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +pub struct BattlepassInfo { + /// Total number of battlepasses per organization. + pub count: u32, + /// Curent active battlepass + pub active: Option, + /// Account of the authorized service which can update users' data + pub bot: Option +} + +#[derive(Encode, Decode, Default, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +/// Reward struct +/// +/// `collection_id`: Collection that will store all claimed Reward-NFTs +pub struct Reward { + pub battlepass_id: Hash, + pub name: BoundedString, + pub cid: BoundedString, + pub level: u8, + pub transferable: bool, + pub collection_id: CollectionId +} + +#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, TypeInfo, MaxEncodedLen)] +pub enum RewardState { + ACTIVE, + INACTIVE, +} +impl Default for RewardState { + fn default() -> Self { + Self::ACTIVE + } +} \ No newline at end of file diff --git a/battlepass/src/weights.rs b/battlepass/src/weights.rs new file mode 100644 index 000000000..c06cb083d --- /dev/null +++ b/battlepass/src/weights.rs @@ -0,0 +1,738 @@ + +//! Autogenerated weights for gamedao_battlepass +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-04-19, STEPS: `20`, REPEAT: `10`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 + +// Executed Command: +// ./target/release/subzero +// benchmark +// pallet +// --execution=wasm +// --pallet=gamedao_battlepass +// --extrinsic=* +// --steps=20 +// --repeat=10 +// --output=modules/gamedao-protocol/battlepass/src/weights.rs +// --template=./.maintain/frame-weight-template.hbs + +#![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 gamedao_battlepass. +pub trait WeightInfo { + fn create_battlepass() -> Weight; + fn update_battlepass() -> Weight; + fn claim_battlepass() -> Weight; + fn activate_battlepass() -> Weight; + fn conclude_battlepass() -> Weight; + fn set_points() -> Weight; + fn create_reward() -> Weight; + fn update_reward() -> Weight; + fn disable_reward() -> Weight; + fn claim_reward() -> Weight; + fn add_level() -> Weight; + fn remove_level() -> Weight; + fn add_bot() -> Weight; +} + +/// Weights for gamedao_battlepass using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: Control OrgStates (r:1 w:0) + /// Proof: Control OrgStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassInfoByOrg (r:1 w:1) + /// Proof: Battlepass BattlepassInfoByOrg (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + /// Storage: Nfts NextCollectionId (r:1 w:1) + /// Proof: Nfts NextCollectionId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) + /// Storage: Nfts CollectionMetadataOf (r:1 w:1) + /// Proof: Nfts CollectionMetadataOf (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassStates (r:0 w:1) + /// Proof: Battlepass BattlepassStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Battlepass Battlepasses (r:0 w:1) + /// Proof: Battlepass Battlepasses (max_values: None, max_size: Some(254), added: 2729, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:0 w:1) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:0 w:1) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts CollectionAccount (r:0 w:1) + /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + fn create_battlepass() -> Weight { + // Proof Size summary in bytes: + // Measured: `566` + // Estimated: `19457` + // Minimum execution time: 102_000 nanoseconds. + Weight::from_parts(106_000_000, 19457) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(9_u64)) + } + /// Storage: Battlepass Battlepasses (r:1 w:1) + /// Proof: Battlepass Battlepasses (max_values: None, max_size: Some(254), added: 2729, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassStates (r:1 w:0) + /// Proof: Battlepass BattlepassStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control OrgStates (r:1 w:0) + /// Proof: Control OrgStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassInfoByOrg (r:1 w:0) + /// Proof: Battlepass BattlepassInfoByOrg (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) + /// Storage: Nfts CollectionMetadataOf (r:1 w:1) + /// Proof: Nfts CollectionMetadataOf (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + fn update_battlepass() -> Weight { + // Proof Size summary in bytes: + // Measured: `1286` + // Estimated: `32273` + // Minimum execution time: 79_000 nanoseconds. + Weight::from_parts(80_000_000, 32273) + .saturating_add(T::DbWeight::get().reads(9_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Battlepass Battlepasses (r:1 w:0) + /// Proof: Battlepass Battlepasses (max_values: None, max_size: Some(254), added: 2729, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassStates (r:1 w:0) + /// Proof: Battlepass BattlepassStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control OrgStates (r:1 w:0) + /// Proof: Control OrgStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassInfoByOrg (r:1 w:0) + /// Proof: Battlepass BattlepassInfoByOrg (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + /// Storage: Nfts Account (r:1 w:1) + /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + /// Storage: Battlepass NftIndex (r:1 w:1) + /// Proof: Battlepass NftIndex (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts Item (r:1 w:1) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1 w:1) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: Nfts ItemMetadataOf (r:1 w:1) + /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(155), added: 2630, mode: MaxEncodedLen) + fn claim_battlepass() -> Weight { + // Proof Size summary in bytes: + // Measured: `1349` + // Estimated: `45207` + // Minimum execution time: 135_000 nanoseconds. + Weight::from_parts(141_000_000, 45207) + .saturating_add(T::DbWeight::get().reads(13_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + /// Storage: Battlepass Battlepasses (r:1 w:0) + /// Proof: Battlepass Battlepasses (max_values: None, max_size: Some(254), added: 2729, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassStates (r:1 w:1) + /// Proof: Battlepass BattlepassStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control OrgStates (r:1 w:0) + /// Proof: Control OrgStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassInfoByOrg (r:1 w:1) + /// Proof: Battlepass BattlepassInfoByOrg (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + fn activate_battlepass() -> Weight { + // Proof Size summary in bytes: + // Measured: `794` + // Estimated: `18085` + // Minimum execution time: 41_000 nanoseconds. + Weight::from_parts(41_000_000, 18085) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Battlepass Battlepasses (r:1 w:0) + /// Proof: Battlepass Battlepasses (max_values: None, max_size: Some(254), added: 2729, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassStates (r:1 w:1) + /// Proof: Battlepass BattlepassStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassInfoByOrg (r:1 w:1) + /// Proof: Battlepass BattlepassInfoByOrg (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + fn conclude_battlepass() -> Weight { + // Proof Size summary in bytes: + // Measured: `790` + // Estimated: `14571` + // Minimum execution time: 35_000 nanoseconds. + Weight::from_parts(38_000_000, 14571) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: Battlepass Battlepasses (r:1 w:0) + /// Proof: Battlepass Battlepasses (max_values: None, max_size: Some(254), added: 2729, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassStates (r:1 w:0) + /// Proof: Battlepass BattlepassStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control OrgStates (r:1 w:0) + /// Proof: Control OrgStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassInfoByOrg (r:1 w:0) + /// Proof: Battlepass BattlepassInfoByOrg (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + /// Storage: Nfts Account (r:2 w:0) + /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + /// Storage: Battlepass Points (r:0 w:1) + /// Proof: Battlepass Points (max_values: None, max_size: Some(100), added: 2575, mode: MaxEncodedLen) + fn set_points() -> Weight { + // Proof Size summary in bytes: + // Measured: `1423` + // Estimated: `24201` + // Minimum execution time: 44_000 nanoseconds. + Weight::from_parts(45_000_000, 24201) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Battlepass Battlepasses (r:1 w:0) + /// Proof: Battlepass Battlepasses (max_values: None, max_size: Some(254), added: 2729, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassStates (r:1 w:0) + /// Proof: Battlepass BattlepassStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control OrgStates (r:1 w:0) + /// Proof: Control OrgStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassInfoByOrg (r:1 w:0) + /// Proof: Battlepass BattlepassInfoByOrg (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + /// Storage: Nfts NextCollectionId (r:1 w:1) + /// Proof: Nfts NextCollectionId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) + /// Storage: Nfts CollectionMetadataOf (r:1 w:1) + /// Proof: Nfts CollectionMetadataOf (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Storage: Battlepass Rewards (r:0 w:1) + /// Proof: Battlepass Rewards (max_values: None, max_size: Some(218), added: 2693, mode: MaxEncodedLen) + /// Storage: Battlepass RewardStates (r:0 w:1) + /// Proof: Battlepass RewardStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:0 w:1) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:0 w:1) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts CollectionAccount (r:0 w:1) + /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + fn create_reward() -> Weight { + // Proof Size summary in bytes: + // Measured: `1197` + // Estimated: `26690` + // Minimum execution time: 112_000 nanoseconds. + Weight::from_parts(113_000_000, 26690) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(8_u64)) + } + /// Storage: Battlepass Rewards (r:1 w:1) + /// Proof: Battlepass Rewards (max_values: None, max_size: Some(218), added: 2693, mode: MaxEncodedLen) + /// Storage: Battlepass RewardStates (r:1 w:0) + /// Proof: Battlepass RewardStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Battlepass Battlepasses (r:1 w:0) + /// Proof: Battlepass Battlepasses (max_values: None, max_size: Some(254), added: 2729, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassStates (r:1 w:0) + /// Proof: Battlepass BattlepassStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control OrgStates (r:1 w:0) + /// Proof: Control OrgStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassInfoByOrg (r:1 w:0) + /// Proof: Battlepass BattlepassInfoByOrg (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) + /// Storage: Nfts CollectionMetadataOf (r:1 w:1) + /// Proof: Nfts CollectionMetadataOf (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + fn update_reward() -> Weight { + // Proof Size summary in bytes: + // Measured: `1632` + // Estimated: `39470` + // Minimum execution time: 90_000 nanoseconds. + Weight::from_parts(93_000_000, 39470) + .saturating_add(T::DbWeight::get().reads(11_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Battlepass Rewards (r:1 w:0) + /// Proof: Battlepass Rewards (max_values: None, max_size: Some(218), added: 2693, mode: MaxEncodedLen) + /// Storage: Battlepass RewardStates (r:1 w:1) + /// Proof: Battlepass RewardStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Battlepass Battlepasses (r:1 w:0) + /// Proof: Battlepass Battlepasses (max_values: None, max_size: Some(254), added: 2729, mode: MaxEncodedLen) + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassInfoByOrg (r:1 w:0) + /// Proof: Battlepass BattlepassInfoByOrg (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + fn disable_reward() -> Weight { + // Proof Size summary in bytes: + // Measured: `946` + // Estimated: `18254` + // Minimum execution time: 37_000 nanoseconds. + Weight::from_parts(38_000_000, 18254) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Battlepass Rewards (r:1 w:0) + /// Proof: Battlepass Rewards (max_values: None, max_size: Some(218), added: 2693, mode: MaxEncodedLen) + /// Storage: Battlepass RewardStates (r:1 w:0) + /// Proof: Battlepass RewardStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Battlepass ClaimedRewards (r:1 w:1) + /// Proof: Battlepass ClaimedRewards (max_values: None, max_size: Some(100), added: 2575, mode: MaxEncodedLen) + /// Storage: Battlepass Battlepasses (r:1 w:0) + /// Proof: Battlepass Battlepasses (max_values: None, max_size: Some(254), added: 2729, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassStates (r:1 w:0) + /// Proof: Battlepass BattlepassStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control OrgStates (r:1 w:0) + /// Proof: Control OrgStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassInfoByOrg (r:1 w:0) + /// Proof: Battlepass BattlepassInfoByOrg (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + /// Storage: Nfts Account (r:2 w:1) + /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + /// Storage: Battlepass Points (r:1 w:0) + /// Proof: Battlepass Points (max_values: None, max_size: Some(100), added: 2575, mode: MaxEncodedLen) + /// Storage: Battlepass Levels (r:2 w:0) + /// Proof: Battlepass Levels (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Battlepass NftIndex (r:1 w:1) + /// Proof: Battlepass NftIndex (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts Item (r:1 w:1) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1 w:1) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: Nfts ItemMetadataOf (r:1 w:1) + /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(155), added: 2630, mode: MaxEncodedLen) + fn claim_reward() -> Weight { + // Proof Size summary in bytes: + // Measured: `2281` + // Estimated: `68175` + // Minimum execution time: 163_000 nanoseconds. + Weight::from_parts(166_000_000, 68175) + .saturating_add(T::DbWeight::get().reads(20_u64)) + .saturating_add(T::DbWeight::get().writes(7_u64)) + } + /// Storage: Battlepass Battlepasses (r:1 w:0) + /// Proof: Battlepass Battlepasses (max_values: None, max_size: Some(254), added: 2729, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassStates (r:1 w:0) + /// Proof: Battlepass BattlepassStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control OrgStates (r:1 w:0) + /// Proof: Control OrgStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassInfoByOrg (r:1 w:0) + /// Proof: Battlepass BattlepassInfoByOrg (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + /// Storage: Battlepass Levels (r:0 w:1) + /// Proof: Battlepass Levels (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + fn add_level() -> Weight { + // Proof Size summary in bytes: + // Measured: `828` + // Estimated: `18085` + // Minimum execution time: 35_000 nanoseconds. + Weight::from_parts(36_000_000, 18085) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Battlepass Battlepasses (r:1 w:0) + /// Proof: Battlepass Battlepasses (max_values: None, max_size: Some(254), added: 2729, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassStates (r:1 w:0) + /// Proof: Battlepass BattlepassStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control OrgStates (r:1 w:0) + /// Proof: Control OrgStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassInfoByOrg (r:1 w:0) + /// Proof: Battlepass BattlepassInfoByOrg (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + /// Storage: Battlepass Levels (r:1 w:1) + /// Proof: Battlepass Levels (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + fn remove_level() -> Weight { + // Proof Size summary in bytes: + // Measured: `922` + // Estimated: `21619` + // Minimum execution time: 40_000 nanoseconds. + Weight::from_parts(41_000_000, 21619) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Battlepass Battlepasses (r:1 w:0) + /// Proof: Battlepass Battlepasses (max_values: None, max_size: Some(254), added: 2729, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassStates (r:1 w:0) + /// Proof: Battlepass BattlepassStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control OrgStates (r:1 w:0) + /// Proof: Control OrgStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassInfoByOrg (r:1 w:1) + /// Proof: Battlepass BattlepassInfoByOrg (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + fn add_bot() -> Weight { + // Proof Size summary in bytes: + // Measured: `794` + // Estimated: `18085` + // Minimum execution time: 32_000 nanoseconds. + Weight::from_parts(33_000_000, 18085) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + /// Storage: Control OrgStates (r:1 w:0) + /// Proof: Control OrgStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassInfoByOrg (r:1 w:1) + /// Proof: Battlepass BattlepassInfoByOrg (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + /// Storage: Nfts NextCollectionId (r:1 w:1) + /// Proof: Nfts NextCollectionId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) + /// Storage: Nfts CollectionMetadataOf (r:1 w:1) + /// Proof: Nfts CollectionMetadataOf (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassStates (r:0 w:1) + /// Proof: Battlepass BattlepassStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Battlepass Battlepasses (r:0 w:1) + /// Proof: Battlepass Battlepasses (max_values: None, max_size: Some(254), added: 2729, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:0 w:1) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:0 w:1) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts CollectionAccount (r:0 w:1) + /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + fn create_battlepass() -> Weight { + // Proof Size summary in bytes: + // Measured: `566` + // Estimated: `19457` + // Minimum execution time: 102_000 nanoseconds. + Weight::from_parts(106_000_000, 19457) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(9_u64)) + } + /// Storage: Battlepass Battlepasses (r:1 w:1) + /// Proof: Battlepass Battlepasses (max_values: None, max_size: Some(254), added: 2729, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassStates (r:1 w:0) + /// Proof: Battlepass BattlepassStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control OrgStates (r:1 w:0) + /// Proof: Control OrgStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassInfoByOrg (r:1 w:0) + /// Proof: Battlepass BattlepassInfoByOrg (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) + /// Storage: Nfts CollectionMetadataOf (r:1 w:1) + /// Proof: Nfts CollectionMetadataOf (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + fn update_battlepass() -> Weight { + // Proof Size summary in bytes: + // Measured: `1286` + // Estimated: `32273` + // Minimum execution time: 79_000 nanoseconds. + Weight::from_parts(80_000_000, 32273) + .saturating_add(RocksDbWeight::get().reads(9_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: Battlepass Battlepasses (r:1 w:0) + /// Proof: Battlepass Battlepasses (max_values: None, max_size: Some(254), added: 2729, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassStates (r:1 w:0) + /// Proof: Battlepass BattlepassStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control OrgStates (r:1 w:0) + /// Proof: Control OrgStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassInfoByOrg (r:1 w:0) + /// Proof: Battlepass BattlepassInfoByOrg (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + /// Storage: Nfts Account (r:1 w:1) + /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + /// Storage: Battlepass NftIndex (r:1 w:1) + /// Proof: Battlepass NftIndex (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts Item (r:1 w:1) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1 w:1) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: Nfts ItemMetadataOf (r:1 w:1) + /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(155), added: 2630, mode: MaxEncodedLen) + fn claim_battlepass() -> Weight { + // Proof Size summary in bytes: + // Measured: `1349` + // Estimated: `45207` + // Minimum execution time: 135_000 nanoseconds. + Weight::from_parts(141_000_000, 45207) + .saturating_add(RocksDbWeight::get().reads(13_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + /// Storage: Battlepass Battlepasses (r:1 w:0) + /// Proof: Battlepass Battlepasses (max_values: None, max_size: Some(254), added: 2729, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassStates (r:1 w:1) + /// Proof: Battlepass BattlepassStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control OrgStates (r:1 w:0) + /// Proof: Control OrgStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassInfoByOrg (r:1 w:1) + /// Proof: Battlepass BattlepassInfoByOrg (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + fn activate_battlepass() -> Weight { + // Proof Size summary in bytes: + // Measured: `794` + // Estimated: `18085` + // Minimum execution time: 41_000 nanoseconds. + Weight::from_parts(41_000_000, 18085) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Battlepass Battlepasses (r:1 w:0) + /// Proof: Battlepass Battlepasses (max_values: None, max_size: Some(254), added: 2729, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassStates (r:1 w:1) + /// Proof: Battlepass BattlepassStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassInfoByOrg (r:1 w:1) + /// Proof: Battlepass BattlepassInfoByOrg (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + fn conclude_battlepass() -> Weight { + // Proof Size summary in bytes: + // Measured: `790` + // Estimated: `14571` + // Minimum execution time: 35_000 nanoseconds. + Weight::from_parts(38_000_000, 14571) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: Battlepass Battlepasses (r:1 w:0) + /// Proof: Battlepass Battlepasses (max_values: None, max_size: Some(254), added: 2729, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassStates (r:1 w:0) + /// Proof: Battlepass BattlepassStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control OrgStates (r:1 w:0) + /// Proof: Control OrgStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassInfoByOrg (r:1 w:0) + /// Proof: Battlepass BattlepassInfoByOrg (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + /// Storage: Nfts Account (r:2 w:0) + /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + /// Storage: Battlepass Points (r:0 w:1) + /// Proof: Battlepass Points (max_values: None, max_size: Some(100), added: 2575, mode: MaxEncodedLen) + fn set_points() -> Weight { + // Proof Size summary in bytes: + // Measured: `1423` + // Estimated: `24201` + // Minimum execution time: 44_000 nanoseconds. + Weight::from_parts(45_000_000, 24201) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Battlepass Battlepasses (r:1 w:0) + /// Proof: Battlepass Battlepasses (max_values: None, max_size: Some(254), added: 2729, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassStates (r:1 w:0) + /// Proof: Battlepass BattlepassStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control OrgStates (r:1 w:0) + /// Proof: Control OrgStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassInfoByOrg (r:1 w:0) + /// Proof: Battlepass BattlepassInfoByOrg (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + /// Storage: Nfts NextCollectionId (r:1 w:1) + /// Proof: Nfts NextCollectionId (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) + /// Storage: Nfts CollectionMetadataOf (r:1 w:1) + /// Proof: Nfts CollectionMetadataOf (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + /// Storage: Battlepass Rewards (r:0 w:1) + /// Proof: Battlepass Rewards (max_values: None, max_size: Some(218), added: 2693, mode: MaxEncodedLen) + /// Storage: Battlepass RewardStates (r:0 w:1) + /// Proof: Battlepass RewardStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:0 w:1) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:0 w:1) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts CollectionAccount (r:0 w:1) + /// Proof: Nfts CollectionAccount (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + fn create_reward() -> Weight { + // Proof Size summary in bytes: + // Measured: `1197` + // Estimated: `26690` + // Minimum execution time: 112_000 nanoseconds. + Weight::from_parts(113_000_000, 26690) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(8_u64)) + } + /// Storage: Battlepass Rewards (r:1 w:1) + /// Proof: Battlepass Rewards (max_values: None, max_size: Some(218), added: 2693, mode: MaxEncodedLen) + /// Storage: Battlepass RewardStates (r:1 w:0) + /// Proof: Battlepass RewardStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Battlepass Battlepasses (r:1 w:0) + /// Proof: Battlepass Battlepasses (max_values: None, max_size: Some(254), added: 2729, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassStates (r:1 w:0) + /// Proof: Battlepass BattlepassStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control OrgStates (r:1 w:0) + /// Proof: Control OrgStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassInfoByOrg (r:1 w:0) + /// Proof: Battlepass BattlepassInfoByOrg (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) + /// Storage: Nfts CollectionMetadataOf (r:1 w:1) + /// Proof: Nfts CollectionMetadataOf (max_values: None, max_size: Some(102), added: 2577, mode: MaxEncodedLen) + fn update_reward() -> Weight { + // Proof Size summary in bytes: + // Measured: `1632` + // Estimated: `39470` + // Minimum execution time: 90_000 nanoseconds. + Weight::from_parts(93_000_000, 39470) + .saturating_add(RocksDbWeight::get().reads(11_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: Battlepass Rewards (r:1 w:0) + /// Proof: Battlepass Rewards (max_values: None, max_size: Some(218), added: 2693, mode: MaxEncodedLen) + /// Storage: Battlepass RewardStates (r:1 w:1) + /// Proof: Battlepass RewardStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Battlepass Battlepasses (r:1 w:0) + /// Proof: Battlepass Battlepasses (max_values: None, max_size: Some(254), added: 2729, mode: MaxEncodedLen) + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassInfoByOrg (r:1 w:0) + /// Proof: Battlepass BattlepassInfoByOrg (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + fn disable_reward() -> Weight { + // Proof Size summary in bytes: + // Measured: `946` + // Estimated: `18254` + // Minimum execution time: 37_000 nanoseconds. + Weight::from_parts(38_000_000, 18254) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Battlepass Rewards (r:1 w:0) + /// Proof: Battlepass Rewards (max_values: None, max_size: Some(218), added: 2693, mode: MaxEncodedLen) + /// Storage: Battlepass RewardStates (r:1 w:0) + /// Proof: Battlepass RewardStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Battlepass ClaimedRewards (r:1 w:1) + /// Proof: Battlepass ClaimedRewards (max_values: None, max_size: Some(100), added: 2575, mode: MaxEncodedLen) + /// Storage: Battlepass Battlepasses (r:1 w:0) + /// Proof: Battlepass Battlepasses (max_values: None, max_size: Some(254), added: 2729, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassStates (r:1 w:0) + /// Proof: Battlepass BattlepassStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control OrgStates (r:1 w:0) + /// Proof: Control OrgStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassInfoByOrg (r:1 w:0) + /// Proof: Battlepass BattlepassInfoByOrg (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + /// Storage: Nfts Account (r:2 w:1) + /// Proof: Nfts Account (max_values: None, max_size: Some(88), added: 2563, mode: MaxEncodedLen) + /// Storage: Battlepass Points (r:1 w:0) + /// Proof: Battlepass Points (max_values: None, max_size: Some(100), added: 2575, mode: MaxEncodedLen) + /// Storage: Battlepass Levels (r:2 w:0) + /// Proof: Battlepass Levels (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Battlepass NftIndex (r:1 w:1) + /// Proof: Battlepass NftIndex (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Nfts CollectionConfigOf (r:1 w:0) + /// Proof: Nfts CollectionConfigOf (max_values: None, max_size: Some(73), added: 2548, mode: MaxEncodedLen) + /// Storage: Nfts Item (r:1 w:1) + /// Proof: Nfts Item (max_values: None, max_size: Some(861), added: 3336, mode: MaxEncodedLen) + /// Storage: Nfts Collection (r:1 w:1) + /// Proof: Nfts Collection (max_values: None, max_size: Some(84), added: 2559, mode: MaxEncodedLen) + /// Storage: Nfts CollectionRoleOf (r:1 w:0) + /// Proof: Nfts CollectionRoleOf (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + /// Storage: Nfts ItemConfigOf (r:1 w:1) + /// Proof: Nfts ItemConfigOf (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: Nfts ItemMetadataOf (r:1 w:1) + /// Proof: Nfts ItemMetadataOf (max_values: None, max_size: Some(155), added: 2630, mode: MaxEncodedLen) + fn claim_reward() -> Weight { + // Proof Size summary in bytes: + // Measured: `2281` + // Estimated: `68175` + // Minimum execution time: 163_000 nanoseconds. + Weight::from_parts(166_000_000, 68175) + .saturating_add(RocksDbWeight::get().reads(20_u64)) + .saturating_add(RocksDbWeight::get().writes(7_u64)) + } + /// Storage: Battlepass Battlepasses (r:1 w:0) + /// Proof: Battlepass Battlepasses (max_values: None, max_size: Some(254), added: 2729, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassStates (r:1 w:0) + /// Proof: Battlepass BattlepassStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control OrgStates (r:1 w:0) + /// Proof: Control OrgStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassInfoByOrg (r:1 w:0) + /// Proof: Battlepass BattlepassInfoByOrg (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + /// Storage: Battlepass Levels (r:0 w:1) + /// Proof: Battlepass Levels (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + fn add_level() -> Weight { + // Proof Size summary in bytes: + // Measured: `828` + // Estimated: `18085` + // Minimum execution time: 35_000 nanoseconds. + Weight::from_parts(36_000_000, 18085) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Battlepass Battlepasses (r:1 w:0) + /// Proof: Battlepass Battlepasses (max_values: None, max_size: Some(254), added: 2729, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassStates (r:1 w:0) + /// Proof: Battlepass BattlepassStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control OrgStates (r:1 w:0) + /// Proof: Control OrgStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassInfoByOrg (r:1 w:0) + /// Proof: Battlepass BattlepassInfoByOrg (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + /// Storage: Battlepass Levels (r:1 w:1) + /// Proof: Battlepass Levels (max_values: None, max_size: Some(69), added: 2544, mode: MaxEncodedLen) + fn remove_level() -> Weight { + // Proof Size summary in bytes: + // Measured: `922` + // Estimated: `21619` + // Minimum execution time: 40_000 nanoseconds. + Weight::from_parts(41_000_000, 21619) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Battlepass Battlepasses (r:1 w:0) + /// Proof: Battlepass Battlepasses (max_values: None, max_size: Some(254), added: 2729, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassStates (r:1 w:0) + /// Proof: Battlepass BattlepassStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control OrgStates (r:1 w:0) + /// Proof: Control OrgStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Battlepass BattlepassInfoByOrg (r:1 w:1) + /// Proof: Battlepass BattlepassInfoByOrg (max_values: None, max_size: Some(118), added: 2593, mode: MaxEncodedLen) + fn add_bot() -> Weight { + // Proof Size summary in bytes: + // Measured: `794` + // Estimated: `18085` + // Minimum execution time: 32_000 nanoseconds. + Weight::from_parts(33_000_000, 18085) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } +} \ No newline at end of file diff --git a/control/Cargo.toml b/control/Cargo.toml index c6ba4934e..37dffb37a 100755 --- a/control/Cargo.toml +++ b/control/Cargo.toml @@ -1,11 +1,6 @@ -# ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -# ███░▄▄▄█░▄▄▀█░▄▀▄░█░▄▄█░▄▀█░▄▄▀█▀▄▄▀██ -# ███░█▄▀█░▀▀░█░█▄█░█░▄▄█░█░█░▀▀░█░██░██ -# ███▄▄▄▄█▄██▄█▄███▄█▄▄▄█▄▄██▄██▄██▄▄███ -# ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ [package] name = "gamedao-control" -version = "1.2.0" +version = "1.3.0" authors = ["zero.io","gamedao.co"] repository = "https://github.com/gamedaoco/gamedao-protocol" edition = "2018" @@ -14,42 +9,41 @@ description = "DAO Factory" [dependencies] hex-literal = "0.3.4" -num_enum = { version = "0.5.1", default-features = false } +num_enum = { version = "0.5.1" } -serde = { version = "1.0.143", optional = true } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +serde = { version = "1.0.143", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } -sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28" } -sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28" } -sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28" } -sp-storage = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28" } +sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } +sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } +sp-storage = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } -frame-support = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28" } -frame-system = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28" } -frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28", default-features = false, optional = true } -sp-io = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.28", default-features=false } +frame-support = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } +frame-system = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false, optional = true } +sp-io = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.43", default-features=false } -pallet-balances = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28" } +pallet-balances = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } -orml-traits = { path = "../../orml/traits", default-features = false } +orml-traits = { git = "https://github.com/open-web3-stack/open-runtime-module-library", branch= "polkadot-v0.9.43", default-features = false } gamedao-traits = { package = "gamedao-traits", path = "../traits", default-features = false } # tangram = { package = "module-tangram", path = "../../zero/tangram", default-features = false } [dev-dependencies] -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28" } -sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28" } -frame-support-test = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.28" } -pallet-balances = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28" } -orml-tokens = { path = "../../orml/tokens", default-features = false } -orml-currencies = { path = "../../orml/currencies", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.43" } +sp-core = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.43" } +pallet-balances = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } +orml-currencies = { git = "https://github.com/open-web3-stack/open-runtime-module-library", branch= "polkadot-v0.9.43", default-features = false } +orml-tokens = { git = "https://github.com/open-web3-stack/open-runtime-module-library", branch= "polkadot-v0.9.43", default-features = false } [features] default = ["std"] runtime-benchmarks = [ - "frame-benchmarking", - "gamedao-traits/frame-benchmarking" + "frame-benchmarking/runtime-benchmarks", + "gamedao-traits/runtime-benchmarks" ] std = [ "codec/std", @@ -62,10 +56,7 @@ std = [ "frame-system/std", "frame-benchmarking/std", - "sp-core/std", "sp-std/std", - "sp-runtime/std", - # "sp-storage/std", # "tangram/std", "orml-traits/std", diff --git a/control/README.md b/control/README.md index a88cf03f3..f7f53109e 100755 --- a/control/README.md +++ b/control/README.md @@ -4,12 +4,14 @@ DAO core to create organizations with their segregated treasury and maintain mem ## Overview -Control is a wrapper for organizationsal bodies in the chain. +Control is a wrapper for organizations on chain. -Organizations consist of members as individual users/accounts. - -Every organization has attached treasury, which is managed collectively by its members with help of related modules. +Organizations consist of: + - members as individual users/accounts + - prime as the prime voter, allowed to call administrative extrinsics + - council as the organization's top voters and controllers +Every organization has a treasury attached, which is managed collectively by its members. ### Terminology @@ -28,7 +30,7 @@ Control is designed to make the following possible: * Allow users to create organizations with specified options. * Provide organizations with individual treasury account. -* Allow users to become a members of organizations. +* Allow users to become members of organizations. ## Interface @@ -56,6 +58,10 @@ The following example shows how to use the Control module in your runtime by exp * Use related module (flow) to raise funds via croudfunding. * Use related module (signal) to manage raised funds via proposals. +## Development + +Run tests in development: `cargo test -p gamedao-control` +Run benchmarking: `cargo test -p gamedao-control --features runtime-benchmarks` ## Related Modules diff --git a/control/src/benchmarking.rs b/control/src/benchmarking.rs index 753fe7319..eb4157ff4 100644 --- a/control/src/benchmarking.rs +++ b/control/src/benchmarking.rs @@ -1,7 +1,9 @@ #![cfg(feature = "runtime-benchmarks")] -use crate::*; -use frame_benchmarking::{account, benchmarks, impl_benchmark_test_suite, whitelisted_caller}; +use super::*; +use crate::Pallet as Control; + +use frame_benchmarking::{account, benchmarks, whitelisted_caller}; use frame_system::RawOrigin; use sp_runtime::{DispatchError, traits::SaturatedConversion}; use sp_std::vec; @@ -46,8 +48,11 @@ benchmarks! { update_org { let caller: T::AccountId = whitelisted_caller(); fund_account::(&caller)?; + let text = BoundedVec::truncate_from((0..255).collect()); let org_id = as ControlBenchmarkingTrait>::create_org(caller.clone()).unwrap(); + let name = Some(text.clone()); + let cid = Some(text.clone()); let prime_id = Some(caller.clone()); let org_type = Some(OrgType::Individual); let access_model = Some(AccessModel::Voting); @@ -55,10 +60,10 @@ benchmarks! { let fee_model = Some(FeeModel::NoFees); let membership_fee: Option = Some(99_u32.saturated_into()); }: _( - RawOrigin::Signed(caller), org_id, prime_id, org_type, access_model.clone(), + RawOrigin::Signed(caller), org_id, name, cid, prime_id, org_type, access_model.clone(), member_limit, fee_model.clone(), membership_fee ) - + verify { let org = Orgs::::get(org_id).unwrap(); assert_eq!(org.membership_fee, membership_fee); @@ -88,7 +93,7 @@ benchmarks! { } add_member { - let r in 1 .. T::MaxMembers::get(); + let r in 1 .. T::MaxMembers::get()-1; // Prepare org creator and members let creator: T::AccountId = whitelisted_caller(); @@ -111,8 +116,24 @@ benchmarks! { assert!(Members::::get(&org_id).contains(&member)); } + update_member_state { + // Prepare org creator and members + let creator: T::AccountId = whitelisted_caller(); + fund_account::(&creator)?; + let org_id = as ControlBenchmarkingTrait>::create_org(creator.clone()).unwrap(); + let member: T::AccountId = account("member", 0, SEED); + fund_account::(&member)?; + + // Add member to org + Pallet::::fill_org_with_members(&org_id, vec![member.clone()])?; + }: _(RawOrigin::Signed(creator), org_id, member.clone(), MemberState::Active) + + verify { + assert!(MemberStates::::get(org_id.clone(), member.clone()) == MemberState::Active); + } + remove_member { - let r in 1 .. T::MaxMembers::get(); + let r in 1 .. T::MaxMembers::get()-1; // Prepare org creator and members let creator: T::AccountId = whitelisted_caller(); @@ -143,7 +164,7 @@ benchmarks! { let currency_id = T::PaymentTokenId::get(); let amount: T::Balance = 300_000_000_000_00_u128.saturated_into(); fund_account::(&treasury_id)?; - + }: _(RawOrigin::Signed(caller), org_id, currency_id, beneficiary.clone(), amount) verify { diff --git a/control/src/lib.rs b/control/src/lib.rs old mode 100755 new mode 100644 index 3b97bcdb0..d7bf4fc19 --- a/control/src/lib.rs +++ b/control/src/lib.rs @@ -17,21 +17,23 @@ pub mod types; mod mock; mod tests; -mod migration; mod benchmarking; pub mod weights; use codec::Codec; use frame_support::{dispatch::{DispatchResult, DispatchError, RawOrigin}, - ensure, PalletId, traits::{Get, StorageVersion}, weights::Weight, BoundedVec, transactional + ensure, PalletId, traits::Get, BoundedVec, transactional, }; -use gamedao_traits::{ControlTrait, ControlBenchmarkingTrait}; +use frame_system::ensure_root; +#[cfg(feature = "runtime-benchmarks")] +use gamedao_traits::ControlBenchmarkingTrait; +use gamedao_traits::ControlTrait; use orml_traits::{MultiCurrency, MultiReservableCurrency}; use scale_info::TypeInfo; use sp_runtime::{ traits::{AccountIdConversion, AtLeast32BitUnsigned, Hash, BadOrigin}, ArithmeticError::Overflow}; -use sp_std::{fmt::Debug, convert::TryInto, vec, vec::{Vec}}; +use sp_std::{fmt::Debug, convert::TryInto, vec, vec::Vec}; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; @@ -53,19 +55,14 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; - /// The current storage version. - const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); - #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] - #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); #[pallet::config] pub trait Config: frame_system::Config { - type Event: From> - + IsType<::Event> - + Into<::Event>; + type RuntimeEvent: From> + + IsType<::RuntimeEvent> + + Into<::RuntimeEvent>; /// The units in which we record balances. type Balance: Member @@ -125,7 +122,7 @@ pub mod pallet { pub(super) type Orgs = StorageMap<_, Blake2_128Concat, T::Hash, Org, OptionQuery>; - /// Org state (Inactive | Active | Locked) by org id. + /// Org state (Inactive | Active | Locked) by org id. /// /// OrgStates: map Hash => OrgState #[pallet::storage] @@ -182,20 +179,19 @@ pub mod pallet { .iter() .for_each(|(creator, prime, treasury_id, name, cid, org_type, access_model, fee_model, membership_fee, gov_currency, pay_currency, member_limit, deposit)| { - let now = frame_system::Pallet::::block_number(); + let now = T::BlockNumber::from(0u32); let index = OrgCount::::get(); let org: Org = types::Org { index, creator: creator.clone(), prime: prime.clone(), name: name.clone(), cid: cid.clone(), org_type: org_type.clone(), fee_model: fee_model.clone(), membership_fee: Some(*membership_fee), - gov_currency: gov_currency.clone(), pay_currency: *pay_currency, member_limit: *member_limit, - access_model: access_model.clone(), created: now.clone(), mutated: now + gov_currency: *gov_currency, pay_currency: *pay_currency, member_limit: *member_limit, + access_model: access_model.clone(), created: now, mutated: now }; let org_id = ::Hashing::hash_of(&org); - Pallet::::do_create_org(org_id.clone(), &org, treasury_id.clone(), *deposit).unwrap(); - Pallet::::do_add_member(org_id, creator.clone(), MemberState::Active).unwrap(); - Pallet::::pay_membership_fee( - &creator, &treasury_id, Some(*membership_fee), fee_model.clone(), *gov_currency).unwrap(); + Pallet::::do_create_org(org_id, &org, treasury_id.clone(), *deposit).expect("Error creating organization in genesis."); + Pallet::::do_add_member(org_id, creator.clone(), MemberState::Active).expect("Error adding member in genesis."); + Pallet::::pay_membership_fee(creator, treasury_id, &org).expect("Error paying membership fee in genesis."); }); } } @@ -227,6 +223,13 @@ pub mod pallet { who: T::AccountId, block_number: T::BlockNumber, }, + /// A member state has been updated + MemberUpdated { + org_id: T::Hash, + who: T::AccountId, + state: MemberState, + block_number: T::BlockNumber, + }, OrgUpdated { org_id: T::Hash, prime_id: Option, @@ -272,20 +275,11 @@ pub mod pallet { WrongOrganizationType } - #[pallet::hooks] - impl Hooks for Pallet { - - fn on_runtime_upgrade() -> Weight { - migration::migrate::() - } - - } - #[pallet::call] impl Pallet { /// Create an on chain organization - /// + /// /// Parameters: /// - `origin`: Org creator. /// - `name`: Org name. @@ -293,7 +287,7 @@ pub mod pallet { /// - `org_type`: Individual | Company | Dao | Hybrid. /// - `access_model`: /// - `fee_model`: - /// + /// /// Optional parameters: /// - `member_limit`: max members. Default: MaxMembers. /// - `member_fee`: fees amount to be applied to new members based on fee model (in `gov_asset` tokens). @@ -321,7 +315,7 @@ pub mod pallet { pay_currency: Option, deposit: Option, ) -> DispatchResult { - let sender = ensure_signed(origin.clone())?; + let sender = ensure_signed(origin)?; // Provide default values for optional parameters: let member_limit = member_limit.unwrap_or(T::MaxMembers::get()); @@ -343,15 +337,15 @@ pub mod pallet { let now = frame_system::Pallet::::block_number(); let org = types::Org { index, creator: sender.clone(), prime: sender.clone(), name, cid, org_type, - fee_model: fee_model.clone(), membership_fee: membership_fee.clone(), gov_currency, - pay_currency, member_limit, access_model, created: now.clone(), mutated: now, + fee_model: fee_model.clone(), membership_fee, gov_currency, + pay_currency, member_limit, access_model, created: now, mutated: now, }; let org_id = T::Hashing::hash_of(&org); - ensure!(!Orgs::::contains_key(&org_id), Error::::OrganizationExists); + ensure!(!Orgs::::contains_key(org_id), Error::::OrganizationExists); Self::do_create_org(org_id, &org, treasury_id.clone(), deposit)?; Self::do_add_member(org_id, sender.clone(), MemberState::Active)?; - Self::pay_membership_fee(&sender, &treasury_id, membership_fee, fee_model, gov_currency)?; + Self::pay_membership_fee(&sender, &treasury_id, &org)?; Ok(()) } @@ -362,7 +356,7 @@ pub mod pallet { /// /// Parameters: /// - `org_id`: Org hash. - /// + /// /// Optional parameters: /// - `prime_id`: new prime id. /// - `access_model`: new access model. @@ -377,6 +371,8 @@ pub mod pallet { pub fn update_org( origin: OriginFor, org_id: T::Hash, + name: Option>, + cid: Option>, prime_id: Option, org_type: Option, access_model: Option, @@ -384,30 +380,35 @@ pub mod pallet { fee_model: Option, membership_fee: Option, ) -> DispatchResult { - let mut org = Orgs::::get(&org_id).ok_or(Error::::OrganizationUnknown)?; + let mut org = Orgs::::get(org_id).ok_or(Error::::OrganizationUnknown)?; + // Create entity like a council Self::ensure_root_or_prime(origin, org.prime.clone(), org.org_type.clone())?; - let args = [prime_id.is_some(), fee_model.is_some(), membership_fee.is_some(), + let args = [ name.is_some(), cid.is_some(), prime_id.is_some(), fee_model.is_some(), membership_fee.is_some(), access_model.is_some(), member_limit.is_some(), org_type.is_some()]; ensure!(args.iter().any(|x| *x == true), Error::::NoChangesProvided); - if let Some(access_model) = access_model.clone() { org.access_model = access_model; }; - if let Some(org_type) = org_type.clone() { org.org_type = org_type; }; - if let Some(member_limit) = member_limit.clone() { org.member_limit = member_limit; }; - if let Some(_) = membership_fee.clone() { org.membership_fee = membership_fee; }; - if let Some(prime_id) = prime_id.clone() { - ensure!(MemberStates::::contains_key(&org_id, &prime_id), Error::::NotMember); + if name.is_some() { org.name = name.clone().unwrap(); }; + if cid.is_some() { org.cid = cid.clone().unwrap(); }; + if access_model.is_some() { org.access_model = access_model.clone().unwrap(); }; + if org_type.is_some() { org.org_type = org_type.clone().unwrap(); }; + if member_limit.is_some() { org.member_limit = member_limit.unwrap(); }; + if membership_fee.is_some() { org.membership_fee = membership_fee; }; + if prime_id.is_some() { + let prime_id = prime_id.clone().unwrap(); + ensure!(MemberStates::::contains_key(org_id, &prime_id), Error::::NotMember); org.prime = prime_id; }; - if let Some(fee_model) = fee_model.clone() { + if fee_model.is_some() { + let fee_model = fee_model.clone().unwrap(); if fee_model != FeeModel::NoFees && membership_fee.is_none() { return Err(Error::::MissingParameter)? }; org.fee_model = fee_model; }; - Orgs::::insert(&org_id, org); - + Orgs::::insert(org_id, org); + let block_number = frame_system::Pallet::::block_number(); Self::deposit_event(Event::OrgUpdated { org_id, prime_id, org_type, access_model, member_limit, @@ -430,10 +431,10 @@ pub mod pallet { /// Weight: `O(1)` #[pallet::weight(T::WeightInfo::enable_org())] pub fn enable_org(origin: OriginFor, org_id: T::Hash) -> DispatchResult { - let org = Orgs::::get(&org_id).ok_or(Error::::OrganizationUnknown)?; + let org = Orgs::::get(org_id).ok_or(Error::::OrganizationUnknown)?; Self::ensure_root_or_prime(origin, org.prime, org.org_type)?; - OrgStates::::insert(org_id.clone(), OrgState::Active); + OrgStates::::insert(org_id, OrgState::Active); Self::deposit_event(Event::OrgEnabled(org_id)); Ok(()) } @@ -442,7 +443,7 @@ pub mod pallet { /// /// Disables an Org to be used and changes it's state to Inactive. /// Allowed origins: Root or prime if OrgType::Individual - /// + /// /// Parameters: /// - `org_id`: Org hash. /// @@ -451,16 +452,16 @@ pub mod pallet { /// Weight: `O(1)` #[pallet::weight(T::WeightInfo::disable_org())] pub fn disable_org(origin: OriginFor, org_id: T::Hash) -> DispatchResult { - let org = Orgs::::get(&org_id).ok_or(Error::::OrganizationUnknown)?; + let org = Orgs::::get(org_id).ok_or(Error::::OrganizationUnknown)?; Self::ensure_root_or_prime(origin, org.prime, org.org_type)?; - OrgStates::::insert(org_id.clone(), OrgState::Inactive); + OrgStates::::insert(org_id, OrgState::Inactive); Self::deposit_event(Event::OrgDisabled(org_id)); Ok(()) } /// Add Member to Org - /// + /// /// Parameters: /// - `org_id`: Org id /// - `who`: Account to be added @@ -474,21 +475,56 @@ pub mod pallet { org_id: T::Hash, who: T::AccountId ) -> DispatchResultWithPostInfo { - let org = Orgs::::get(&org_id).ok_or(Error::::OrganizationUnknown)?; - Self::ensure_membership_permissions(origin, who.clone(), org.prime.clone(), org.org_type.clone(), org.access_model.clone())?; + let org = Orgs::::get(org_id).ok_or(Error::::OrganizationUnknown)?; + if let Ok(sender) = ensure_signed(origin.clone()) { + ensure!( + sender == who || (org.access_model == AccessModel::Prime && sender == org.prime), + BadOrigin + ); + } + else { + ensure_root(origin)?; + } let member_state = match org.access_model { AccessModel::Open => MemberState::Active, _ => MemberState::Pending, }; let treasury_id = OrgTreasury::::get(org_id).ok_or(Error::::TreasuryUnknown)?; - Self::pay_membership_fee(&org.prime, &treasury_id, org.membership_fee, org.fee_model, org.gov_currency)?; + Self::pay_membership_fee(&org.prime, &treasury_id, &org)?; let members_count = Self::do_add_member(org_id, who.clone(), member_state)?; Ok(Some(T::WeightInfo::add_member(members_count)).into()) } + /// Update member state in the organization + /// + /// Parameters: + /// - `org_id`: Org id + /// - `who`: Account to change state for + /// - `state`: new state value + /// + /// Emits `MemberUpdated` event when successful. + /// + /// Weight: `O(log n)` + #[pallet::weight(T::WeightInfo::update_member_state())] + pub fn update_member_state( + origin: OriginFor, + org_id: T::Hash, + who: T::AccountId, + state: MemberState + ) -> DispatchResult { + let org = Orgs::::get(org_id).ok_or(Error::::OrganizationUnknown)?; + Self::ensure_membership_permissions(origin, who.clone(), org.prime.clone(), org.org_type.clone(), org.access_model)?; + + let current_member_state = MemberStates::::get(org_id, who.clone()); + if current_member_state == MemberState::Pending { + Self::do_update_member(org_id, who.clone(), state)?; + } + Ok(()) + } + /// Remove member from Org - /// + /// /// Parameters: /// - `org_id`: Org id /// - `who`: Account to be removed @@ -498,18 +534,18 @@ pub mod pallet { /// Weight: `O(log n)` #[pallet::weight(T::WeightInfo::remove_member(T::MaxMembers::get()))] pub fn remove_member(origin: OriginFor, org_id: T::Hash, who: T::AccountId) -> DispatchResultWithPostInfo { - let org = Orgs::::get(&org_id).ok_or(Error::::OrganizationUnknown)?; + let org = Orgs::::get(org_id).ok_or(Error::::OrganizationUnknown)?; Self::ensure_membership_permissions(origin, who.clone(), org.prime.clone(), org.org_type.clone(), org.access_model.clone())?; - let member_count = Self::do_remove_member(org_id.clone(), who.clone())?; + let member_count = Self::do_remove_member(org_id, who.clone())?; if org.fee_model == FeeModel::Reserve { - T::Currency::unreserve(org.gov_currency, &who, org.membership_fee.unwrap()); + T::Currency::unreserve(org.gov_currency, &who, org.membership_fee.ok_or(Error::::MissingParameter)?); } Ok(Some(T::WeightInfo::remove_member(member_count)).into()) } /// Make spending from the org treasury - /// + /// /// Allowed origins: Root or prime if OrgType::Individual /// /// Parameters: @@ -530,13 +566,13 @@ pub mod pallet { beneficiary: T::AccountId, amount: T::Balance ) -> DispatchResult { - let org = Orgs::::get(&org_id).ok_or(Error::::OrganizationUnknown)?; + let org = Orgs::::get(org_id).ok_or(Error::::OrganizationUnknown)?; let treasury_id = OrgTreasury::::get(org_id).ok_or(Error::::TreasuryUnknown)?; Self::ensure_root_or_prime(origin, org.prime, org.org_type)?; T::Currency::transfer(currency_id, &treasury_id, &beneficiary, amount ).map_err(|_| Error::::BalanceLow)?; - + let block_number = frame_system::Pallet::::block_number(); Self::deposit_event(Event::FundsSpended { org_id, beneficiary, amount, currency_id, block_number }); @@ -550,17 +586,17 @@ impl Pallet { fn do_create_org(org_id: T::Hash, org: &Org, treasury_id: T::AccountId, deposit: T::Balance, ) -> Result<(), DispatchError> { let creator = org.creator.clone(); - let created_at = org.created.clone(); + let created_at = org.created; - OrgTreasury::::insert(&org_id, &treasury_id); - OrgStates::::insert(&org_id, OrgState::Active); + OrgTreasury::::insert(org_id, &treasury_id); + OrgStates::::insert(org_id, OrgState::Active); OrgCount::::set(org.index.checked_add(1).ok_or(Overflow)?); T::Currency::transfer( org.gov_currency, &creator, &treasury_id, deposit, ).map_err(|_| Error::::BalanceLow)?; - Orgs::::insert(&org_id, org); + Orgs::::insert(org_id, org); Self::deposit_event(Event::OrgCreated { org_id, creator, treasury_id, created_at, realm_index: 0 }); Ok(()) @@ -568,32 +604,44 @@ impl Pallet { fn do_add_member(org_id: T::Hash, who: T::AccountId, member_state: MemberState ) -> Result { - let mut members = Members::::get(&org_id); + let mut members = Members::::get(org_id); let location = members.binary_search(&who).err().ok_or(Error::::AlreadyMember)?; members .try_insert(location, who.clone()) .map_err(|_| Error::::MembershipLimitReached)?; let members_count = members.len() as u32; - Members::::insert(&org_id, &members); - OrgMemberCount::::insert(&org_id, members_count); - MemberStates::::insert(&org_id, &who, member_state); - + // TODO: flatten Members and MemberStates + Members::::insert(org_id, &members); + OrgMemberCount::::insert(org_id, members_count); + MemberStates::::insert(org_id, &who, member_state); + let block_number = frame_system::Pallet::::block_number(); Self::deposit_event(Event::MemberAdded { org_id, who, block_number }); - + Ok(members_count) } + fn do_update_member( + org_id: T::Hash, + who: T::AccountId, + state: MemberState + ) -> Result<(), DispatchError> { + MemberStates::::set(org_id, &who, state.clone()); + let block_number = frame_system::Pallet::::block_number(); + Self::deposit_event(Event::MemberUpdated { org_id, who, state, block_number }); + Ok(()) + } + fn do_remove_member(org_id: T::Hash, who: T::AccountId) -> Result { - let mut members = Members::::get(&org_id); + let mut members = Members::::get(org_id); let location = members.binary_search(&who).ok().ok_or(Error::::NotMember)?; members.remove(location); let members_count = members.len() as u32; - Members::::insert(&org_id, &members); - OrgMemberCount::::insert(&org_id, members_count); - MemberStates::::remove(&org_id, &who); + Members::::insert(org_id, &members); + OrgMemberCount::::insert(org_id, members_count); + MemberStates::::remove(org_id, &who); let block_number = frame_system::Pallet::::block_number(); Self::deposit_event(Event::MemberRemoved { org_id, who, block_number }); @@ -604,19 +652,17 @@ impl Pallet { fn pay_membership_fee( who: &T::AccountId, treasury_id: &T::AccountId, - fee: Option, - fee_model: FeeModel, - gov_currency_id: T::CurrencyId + org: &Org ) -> Result<(), DispatchError> { - match fee_model { + match org.fee_model { FeeModel::NoFees => {}, FeeModel::Reserve => { - T::Currency::reserve(gov_currency_id, &who, fee.unwrap() + T::Currency::reserve(org.gov_currency, who, org.membership_fee.unwrap() ).map_err(|_| Error::::BalanceLow)?; }, FeeModel::Transfer => { T::Currency::transfer( - gov_currency_id, &who, &treasury_id, fee.unwrap() + org.gov_currency, who, treasury_id, org.membership_fee.unwrap() ).map_err(|_| Error::::BalanceLow)?; } }; @@ -624,7 +670,7 @@ impl Pallet { } fn ensure_membership_permissions( - origin: T::Origin, + origin: T::RuntimeOrigin, who: T::AccountId, prime: T::AccountId, org_type: OrgType, @@ -632,22 +678,24 @@ impl Pallet { ) -> Result<(), BadOrigin> { match access_model { AccessModel::Open => { - return Ok(Self::ensure_root_or_self(origin, who.clone())?); + Self::ensure_root_or_self(origin, who) }, AccessModel::Prime => { - return Ok(Self::ensure_root_or_prime(origin, prime, org_type)?); + Self::ensure_root_or_prime(origin, prime, org_type) }, AccessModel::Voting => { - return Ok(Self::ensure_root_or_governance(origin)?); + Self::ensure_root_or_prime(origin, prime, org_type) + // TODO: add voting when membership voting is available + // return Ok(Self::ensure_root_or_governance(origin)?); }, } } - fn ensure_root_or_prime(origin: T::Origin, prime: T::AccountId, org_type: OrgType) -> Result<(), BadOrigin> { + fn ensure_root_or_prime(origin: T::RuntimeOrigin, prime: T::AccountId, _org_type: OrgType) -> Result<(), BadOrigin> { match origin.into() { Ok(RawOrigin::Root) => Ok(()), Ok(RawOrigin::Signed(t)) => { - if org_type == OrgType::Individual && t == prime { + if t == prime { return Ok(()); } Err(BadOrigin) @@ -656,15 +704,15 @@ impl Pallet { } } - fn ensure_root_or_governance(origin: T::Origin) -> Result<(), BadOrigin> { - match origin.into() { - Ok(RawOrigin::Root) => Ok(()), - // TODO: implement governance origin type - _ => Err(BadOrigin), - } - } + // fn ensure_root_or_governance(origin: T::RuntimeOrigin) -> Result<(), BadOrigin> { + // match origin.into() { + // Ok(RawOrigin::Root) => Ok(()), + // // TODO: implement governance origin type + // _ => Err(BadOrigin), + // } + // } - fn ensure_root_or_self(origin: T::Origin, who: T::AccountId) -> Result<(), BadOrigin> { + fn ensure_root_or_self(origin: T::RuntimeOrigin, who: T::AccountId) -> Result<(), BadOrigin> { match origin.into() { Ok(RawOrigin::Root) => Ok(()), Ok(RawOrigin::Signed(t)) => { @@ -684,8 +732,10 @@ impl ControlTrait for Pallet { fn org_prime_account(org_id: &T::Hash) -> Option { if let Some(org) = Orgs::::get(org_id) { - return Some(org.prime) - } else { return None } + Some(org.prime) + } else { + None + } } fn org_treasury_account(org_id: &T::Hash) -> Option { OrgTreasury::::get(org_id) @@ -701,9 +751,8 @@ impl ControlTrait for Pallet { } } +#[cfg(feature = "runtime-benchmarks")] impl ControlBenchmarkingTrait for Pallet { - /// ** Should be used for benchmarking only!!! ** - #[cfg(feature = "runtime-benchmarks")] fn create_org(caller: T::AccountId) -> Result { let text = BoundedVec::truncate_from(vec![1, 2, 3, 4]); let index = OrgCount::::get(); @@ -724,8 +773,6 @@ impl ControlBenchmarkingTrait for Pallet { Ok(org_id) } - /// ** Should be used for benchmarking only!!! ** - #[cfg(feature = "runtime-benchmarks")] fn fill_org_with_members(org_id: &T::Hash, accounts: Vec) -> Result<(), DispatchError> { for acc in BoundedVec::::truncate_from(accounts) { Pallet::::add_member( @@ -733,6 +780,12 @@ impl ControlBenchmarkingTrait for Pallet { org_id.clone(), acc.clone() ).unwrap(); + Pallet::::update_member_state( + frame_system::RawOrigin::Root.into(), + org_id.clone(), + acc.clone(), + MemberState::Active + ).unwrap(); } Ok(()) } diff --git a/control/src/migration.rs b/control/src/migration.rs deleted file mode 100644 index 1af62394c..000000000 --- a/control/src/migration.rs +++ /dev/null @@ -1,34 +0,0 @@ -use frame_support::{ - traits::{Get, GetStorageVersion, PalletInfoAccess, StorageVersion}, - Blake2_128Concat, - BoundedVec -}; -use sp_std::prelude::*; -use crate::{Config, Pallet, Weight}; - - -pub fn migrate() -> Weight { - - let version = StorageVersion::get::>(); - let mut weight: Weight = 0; - - if version < 1 { - weight = weight.saturating_add(v1::migrate::()); - StorageVersion::new(1).put::>(); - } - - weight -} - -mod v1 { - use super::*; - use sp_io::hashing::twox_128; - - pub fn migrate() -> Weight { - let _ = frame_support::storage::unhashed::clear_prefix( - &twox_128(>::name().as_bytes()), None, None - ); - - T::DbWeight::get().writes(1) - } -} diff --git a/control/src/mock.rs b/control/src/mock.rs index c78d68a57..583649d50 100644 --- a/control/src/mock.rs +++ b/control/src/mock.rs @@ -1,7 +1,7 @@ #![cfg(test)] use crate as pallet_control; -use frame_support::{PalletId, {traits::GenesisBuild}, pallet_prelude::*}; +use frame_support::{PalletId, {traits::GenesisBuild}, pallet_prelude::*, traits::Nothing}; use frame_system; use codec::MaxEncodedLen; use sp_core::H256; @@ -39,12 +39,10 @@ pub const PAYMENT_TOKEN_ID: CurrencyId = 2; // Accounts: pub const TREASURY: AccountId = 1; -pub const GAME3_TREASURY: AccountId = 2; pub const GAMEDAO_TREASURY: AccountId = 3; pub const ALICE: AccountId = 4; pub const BOB: AccountId = 5; pub const CHARLIE: AccountId = 6; -pub const DAVE: AccountId = 7; // Configure a mock runtime to test the pallet. frame_support::construct_runtime!( @@ -64,8 +62,6 @@ frame_support::construct_runtime!( frame_support::parameter_types! { pub const BlockHashCount: u32 = 250; pub const SS58Prefix: u8 = 42; - pub BlockWeights: frame_system::limits::BlockWeights = - frame_system::limits::BlockWeights::simple_max(1024); } impl frame_system::Config for Test { @@ -73,8 +69,8 @@ impl frame_system::Config for Test { type BlockWeights = (); type BlockLength = (); type DbWeight = (); - type Origin = Origin; - type Call = Call; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; type Index = u64; type BlockNumber = u64; type Hash = Hash; @@ -82,7 +78,7 @@ impl frame_system::Config for Test { type AccountId = AccountId; type Lookup = IdentityLookup; type Header = Header; - type Event = Event; + type RuntimeEvent = RuntimeEvent; type BlockHashCount = BlockHashCount; type Version = (); type PalletInfo = PalletInfo; @@ -104,19 +100,17 @@ orml_traits::parameter_type_with_key! { }; } impl orml_tokens::Config for Test { - type Event = Event; + type RuntimeEvent = RuntimeEvent; type Balance = Balance; type Amount = Amount; type CurrencyId = CurrencyId; type WeightInfo = (); type ExistentialDeposits = ExistentialDeposits; - type OnDust = (); + type CurrencyHooks = (); type MaxLocks = (); - type DustRemovalWhitelist = frame_support::traits::Nothing; - type OnNewTokenAccount = (); - type OnKilledTokenAccount = (); - type ReserveIdentifier = ReserveIdentifier; type MaxReserves = MaxReserves; + type ReserveIdentifier = ReserveIdentifier; + type DustRemovalWhitelist = Nothing; } frame_support::parameter_types! { @@ -125,11 +119,11 @@ frame_support::parameter_types! { impl pallet_balances::Config for Test { type Balance = Balance; type DustRemoval = (); - type Event = Event; + type RuntimeEvent = RuntimeEvent; type ExistentialDeposit = ExistentialDeposit; type AccountStore = frame_system::Pallet; type MaxLocks = (); - type MaxReserves = MaxReserves; + type MaxReserves = (); type ReserveIdentifier = ReserveIdentifier; type WeightInfo = (); } @@ -146,14 +140,16 @@ frame_support::parameter_types! { pub const PaymentTokenId: CurrencyId = PAYMENT_TOKEN_ID; pub const MinimumDeposit: Balance = 5 * DOLLARS; pub const ControlPalletId: PalletId = PalletId(*b"gd/cntrl"); + pub const MaxMembers: u32 = 10000; + pub const StringLimit: u32 = 64; } impl pallet_control::Config for Test { type Balance = Balance; type CurrencyId = CurrencyId; type WeightInfo = (); - type Event = Event; + type RuntimeEvent = RuntimeEvent; type Currency = Currencies; - type MaxMembers = ConstU32<10000>; + type MaxMembers = MaxMembers; type ProtocolTokenId = ProtocolTokenId; type PaymentTokenId = PaymentTokenId; type MinimumDeposit = MinimumDeposit; diff --git a/control/src/tests.rs b/control/src/tests.rs index 31abc1cbe..1963d4e26 100644 --- a/control/src/tests.rs +++ b/control/src/tests.rs @@ -1,10 +1,10 @@ #![cfg(test)] use frame_support::{assert_noop, assert_ok}; -use sp_runtime::traits::{BadOrigin}; -use sp_core::H256; +use sp_runtime::traits::BadOrigin; +use sp_core::{H256, ConstU32}; use super::*; -use mock::{new_test_ext, System, Test, Event, Control, Origin, Tokens, CurrencyId, Balance, AccountId, +use mock::{new_test_ext, System, Test, RuntimeEvent as Event, Control, RuntimeOrigin as Origin, Tokens, CurrencyId, Balance, AccountId, ALICE, BOB, CHARLIE, PAYMENT_TOKEN_ID, PROTOCOL_TOKEN_ID, DOLLARS}; @@ -42,7 +42,7 @@ fn control_create_org() { Origin::signed(ALICE), bounded_str.clone(), bounded_str.clone(), OrgType::Company, AccessModel::Prime, FeeModel::NoFees, None, None, None, None, None), Error::::WrongOrganizationType); - + // Create org with org type Hybrid // Error: WrongOrganizationType assert_noop!(Control::create_org( @@ -64,7 +64,7 @@ fn control_create_org() { AccessModel::Prime, FeeModel::Transfer, None, None, None, None, None), Error::::MissingParameter); - // Check if creator (sender) has enough protocol token free balance + // Check if creator (sender) has enough protocol token free balance // to make a deposit into org's treasury // Error: BalanceLow assert_noop!(Control::create_org( @@ -92,41 +92,48 @@ fn control_create_org() { #[test] fn control_update_org() { new_test_ext().execute_with(|| { + let current_block = 3; System::set_block_number(current_block); let org_id = create_org(AccessModel::Prime); + let bounded_str: BoundedVec> = BoundedVec::truncate_from(vec![1,2]); + let new_name: BoundedVec> = bounded_str.clone(); + let new_cid: BoundedVec> = bounded_str.clone(); + // Check if no changes were provided // Error: NoChangesProvided assert_noop!(Control::update_org( - Origin::signed(ALICE), org_id, None, None, None, None, None, None), + Origin::signed(ALICE), org_id, None, None, None, None, None, None, None, None), Error::::NoChangesProvided); // FeeModel::Transfer and no membership_fee provided // Error: NoChangesProvided assert_noop!(Control::update_org( - Origin::signed(ALICE), org_id, None, None, None, None, Some(FeeModel::Transfer), None), + Origin::signed(ALICE), org_id, None, None, None, None, None, None, Some(FeeModel::Transfer), None), Error::::MissingParameter); - + // Check if prime can be not a member // Error: NotMember assert_noop!(Control::update_org( - Origin::signed(ALICE), org_id, Some(BOB), None, None, None, None, None), + Origin::signed(ALICE), org_id, None, None, Some(BOB), None, None, None, None, None), Error::::NotMember); assert_ok!(Control::add_member(Origin::signed(ALICE), org_id, BOB)); - + // Check if only prime can perform update_org // Error: BadOrigin assert_noop!(Control::update_org( - Origin::signed(BOB), org_id, None, Some(OrgType::Dao), None, None, None, None), + Origin::signed(BOB), org_id, None, None, None, Some(OrgType::Dao), None, None, None, None), BadOrigin); - + // Check if root can update - assert_ok!(Control::update_org(Origin::root(), org_id, None, None, None, None, None, Some(199 * DOLLARS))); + assert_ok!(Control::update_org(Origin::root(), org_id, None, None, None, None, None, None, None, Some(199 * DOLLARS))); // Check if update_org works as expected let prime_id = Some(BOB); + let name: Option>> = Some(new_name); + let cid: Option>> = Some(new_cid); let org_type = Some(OrgType::Dao); let access_model = Some(AccessModel::Voting); let member_limit = Some(100 as MemberLimit); @@ -134,11 +141,13 @@ fn control_update_org() { let membership_fee = Some(99 * DOLLARS); assert_ok!(Control::update_org( - Origin::signed(ALICE), org_id, prime_id, org_type.clone(), access_model.clone(), member_limit, + Origin::signed(ALICE), org_id, name.clone(), cid.clone(), prime_id, org_type.clone(), access_model.clone(), member_limit, fee_model.clone(), membership_fee)); let org = Orgs::::get(org_id).unwrap(); assert_eq!(org.prime, prime_id.clone().unwrap()); + assert_eq!(org.name, name.clone().unwrap()); + assert_eq!(org.cid, cid.clone().unwrap()); assert_eq!(org.org_type, org_type.clone().unwrap()); assert_eq!(org.access_model, access_model.clone().unwrap()); assert_eq!(org.member_limit, member_limit.unwrap()); @@ -152,12 +161,12 @@ fn control_update_org() { } ) ); - + }) } #[test] -fn control_enable_deisable_org() { +fn control_enable_disable_org() { new_test_ext().execute_with(|| { let current_block = 3; System::set_block_number(current_block); @@ -166,12 +175,12 @@ fn control_enable_deisable_org() { assert_noop!(Control::disable_org(Origin::signed(BOB), org_id), BadOrigin); assert_ok!(Control::disable_org(Origin::root(), org_id)); assert_eq!(OrgStates::::get(org_id), OrgState::Inactive); - System::assert_has_event(mock::Event::Control(crate::Event::OrgDisabled(org_id))); + System::assert_has_event(Event::Control(crate::Event::OrgDisabled(org_id))); // Enable org root assert_noop!(Control::enable_org(Origin::signed(BOB), org_id), BadOrigin); assert_ok!(Control::enable_org(Origin::root(), org_id)); assert_eq!(OrgStates::::get(org_id), OrgState::Active); - System::assert_has_event(mock::Event::Control(crate::Event::OrgEnabled(org_id))); + System::assert_has_event(Event::Control(crate::Event::OrgEnabled(org_id))); // Disable org prime assert_ok!(Control::disable_org(Origin::signed(ALICE), org_id)); assert_eq!(OrgStates::::get(org_id), OrgState::Inactive); @@ -193,7 +202,7 @@ fn control_add_remove_member_access_prime() { assert!(Members::::get(org_id).contains(&CHARLIE)); assert_noop!(Control::add_member(Origin::signed(ALICE), org_id, CHARLIE), Error::::AlreadyMember); System::assert_has_event( - mock::Event::Control(crate::Event::MemberAdded{ + Event::Control(crate::Event::MemberAdded{ org_id, who: CHARLIE, block_number: current_block }) ); @@ -203,7 +212,7 @@ fn control_add_remove_member_access_prime() { assert!(!Members::::get(org_id).contains(&CHARLIE)); assert_noop!(Control::remove_member(Origin::signed(ALICE), org_id, CHARLIE), Error::::NotMember); System::assert_has_event( - mock::Event::Control(crate::Event::MemberRemoved{ + Event::Control(crate::Event::MemberRemoved{ org_id, who: CHARLIE, block_number: current_block }) ); @@ -215,6 +224,11 @@ fn control_add_remove_member_access_prime() { assert_ok!(Control::remove_member(Origin::root(), org_id, CHARLIE)); assert!(!Members::::get(org_id).contains(&CHARLIE)); + // Add member signed + assert_ok!(Control::add_member(Origin::signed(CHARLIE), org_id, CHARLIE)); + assert!(Members::::get(org_id).contains(&CHARLIE)); + assert_eq!(MemberStates::::get(org_id, CHARLIE), MemberState::Pending); + // TODO: since membership_fee logic is unclear for the moment, there is no tests for it yet }) } @@ -292,7 +306,7 @@ fn control_spend_funds() { Control::spend_funds(Origin::signed(BOB), org_id, PAYMENT_TOKEN_ID, beneficiary, amount), BadOrigin); System::assert_has_event( - mock::Event::Control(crate::Event::FundsSpended{ + Event::Control(crate::Event::FundsSpended{ org_id, beneficiary, amount, currency_id, block_number: current_block }) ); diff --git a/control/src/weights.rs b/control/src/weights.rs index cfba7b00c..50bac8f88 100644 --- a/control/src/weights.rs +++ b/control/src/weights.rs @@ -1,35 +1,21 @@ -// This file is part of Substrate. - -// Copyright (C) 2021 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. //! Autogenerated weights for gamedao_control //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-09-13, STEPS: `20`, REPEAT: 10, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 +//! DATE: 2023-04-19, STEPS: `20`, REPEAT: `10`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 // Executed Command: // ./target/release/subzero // benchmark // pallet +// --execution=wasm // --pallet=gamedao_control // --extrinsic=* // --steps=20 // --repeat=10 -// --output=gamedao-protocol/control/src/weights.rs +// --output=modules/gamedao-protocol/control/src/weights.rs // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -46,6 +32,7 @@ pub trait WeightInfo { fn disable_org() -> Weight; fn enable_org() -> Weight; fn add_member(r: u32, ) -> Weight; + fn update_member_state() -> Weight; fn remove_member(r: u32, ) -> Weight; fn spend_funds() -> Weight; } @@ -53,142 +40,282 @@ pub trait WeightInfo { /// Weights for gamedao_control using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Control OrgCount (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: Control Orgs (r:1 w:1) - // Storage: Tokens Accounts (r:2 w:2) - // Storage: Control Members (r:1 w:1) - // Storage: Control OrgMemberCount (r:0 w:1) - // Storage: Control OrgTreasury (r:0 w:1) - // Storage: Control MemberStates (r:0 w:1) - // Storage: Control OrgStates (r:0 w:1) + /// Storage: Control OrgCount (r:1 w:1) + /// Proof: Control OrgCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Control Orgs (r:1 w:1) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Tokens Accounts (r:2 w:2) + /// Proof: Tokens Accounts (max_values: None, max_size: Some(109), added: 2584, mode: MaxEncodedLen) + /// Storage: Control Members (r:1 w:1) + /// Proof: Control Members (max_values: None, max_size: Some(32050), added: 34525, mode: MaxEncodedLen) + /// Storage: Control OrgMemberCount (r:0 w:1) + /// Proof: Control OrgMemberCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: Control OrgTreasury (r:0 w:1) + /// Proof: Control OrgTreasury (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Control MemberStates (r:0 w:1) + /// Proof: Control MemberStates (max_values: None, max_size: Some(97), added: 2572, mode: MaxEncodedLen) + /// Storage: Control OrgStates (r:0 w:1) + /// Proof: Control OrgStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn create_org() -> Weight { - (54_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(6 as Weight)) - .saturating_add(T::DbWeight::get().writes(10 as Weight)) + // Proof Size summary in bytes: + // Measured: `377` + // Estimated: `50510` + // Minimum execution time: 82_000 nanoseconds. + Weight::from_parts(85_000_000, 50510) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(10_u64)) } - // Storage: Control Orgs (r:1 w:1) - // Storage: Control MemberStates (r:1 w:0) + /// Storage: Control Orgs (r:1 w:1) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Control MemberStates (r:1 w:0) + /// Proof: Control MemberStates (max_values: None, max_size: Some(97), added: 2572, mode: MaxEncodedLen) fn update_org() -> Weight { - (22_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + // Proof Size summary in bytes: + // Measured: `534` + // Estimated: `7317` + // Minimum execution time: 26_000 nanoseconds. + Weight::from_parts(27_000_000, 7317) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Control Orgs (r:1 w:0) - // Storage: Control OrgStates (r:0 w:1) + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Control OrgStates (r:0 w:1) + /// Proof: Control OrgStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn disable_org() -> Weight { - (18_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(1 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + // Proof Size summary in bytes: + // Measured: `449` + // Estimated: `3755` + // Minimum execution time: 19_000 nanoseconds. + Weight::from_parts(20_000_000, 3755) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: Control Orgs (r:1 w:0) - // Storage: Control OrgStates (r:0 w:1) + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Control OrgStates (r:0 w:1) + /// Proof: Control OrgStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn enable_org() -> Weight { - (18_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(1 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) - } - // Storage: Control Orgs (r:1 w:0) - // Storage: Control OrgTreasury (r:1 w:0) - // Storage: Control Members (r:1 w:1) - // Storage: Control OrgMemberCount (r:0 w:1) - // Storage: Control MemberStates (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `449` + // Estimated: `3755` + // Minimum execution time: 18_000 nanoseconds. + Weight::from_parts(19_000_000, 3755) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Control OrgTreasury (r:1 w:0) + /// Proof: Control OrgTreasury (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Control Members (r:1 w:1) + /// Proof: Control Members (max_values: None, max_size: Some(32050), added: 34525, mode: MaxEncodedLen) + /// Storage: Control OrgMemberCount (r:0 w:1) + /// Proof: Control OrgMemberCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: Control MemberStates (r:0 w:1) + /// Proof: Control MemberStates (max_values: None, max_size: Some(97), added: 2572, mode: MaxEncodedLen) + /// The range of component `r` is `[1, 999]`. fn add_member(r: u32, ) -> Weight { - (27_200_000 as Weight) - // Standard Error: 2_000 - .saturating_add((36_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(3 as Weight)) - .saturating_add(T::DbWeight::get().writes(3 as Weight)) - } - // Storage: Control Orgs (r:1 w:0) - // Storage: Control Members (r:1 w:1) - // Storage: Control OrgMemberCount (r:0 w:1) - // Storage: Control MemberStates (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `597 + r * (32 ±0)` + // Estimated: `42815` + // Minimum execution time: 32_000 nanoseconds. + Weight::from_parts(31_011_478, 42815) + // Standard Error: 933 + .saturating_add(Weight::from_ref_time(36_876).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Control MemberStates (r:1 w:0) + /// Proof: Control MemberStates (max_values: None, max_size: Some(97), added: 2572, mode: MaxEncodedLen) + fn update_member_state() -> Weight { + // Proof Size summary in bytes: + // Measured: `573` + // Estimated: `7317` + // Minimum execution time: 16_000 nanoseconds. + Weight::from_parts(17_000_000, 7317) + .saturating_add(T::DbWeight::get().reads(2_u64)) + } + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Control Members (r:1 w:1) + /// Proof: Control Members (max_values: None, max_size: Some(32050), added: 34525, mode: MaxEncodedLen) + /// Storage: Control OrgMemberCount (r:0 w:1) + /// Proof: Control OrgMemberCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: Control MemberStates (r:0 w:1) + /// Proof: Control MemberStates (max_values: None, max_size: Some(97), added: 2572, mode: MaxEncodedLen) + /// The range of component `r` is `[1, 999]`. fn remove_member(r: u32, ) -> Weight { - (24_323_000 as Weight) - // Standard Error: 2_000 - .saturating_add((31_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(3 as Weight)) - } - // Storage: Control Orgs (r:1 w:0) - // Storage: Control OrgTreasury (r:1 w:0) - // Storage: Tokens Accounts (r:2 w:2) - // Storage: System Account (r:2 w:1) + // Proof Size summary in bytes: + // Measured: `522 + r * (32 ±0)` + // Estimated: `39270` + // Minimum execution time: 28_000 nanoseconds. + Weight::from_parts(27_753_161, 39270) + // Standard Error: 784 + .saturating_add(Weight::from_ref_time(31_088).saturating_mul(r.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Control OrgTreasury (r:1 w:0) + /// Proof: Control OrgTreasury (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Tokens Accounts (r:2 w:2) + /// Proof: Tokens Accounts (max_values: None, max_size: Some(109), added: 2584, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn spend_funds() -> Weight { - (45_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(6 as Weight)) - .saturating_add(T::DbWeight::get().writes(3 as Weight)) + // Proof Size summary in bytes: + // Measured: `946` + // Estimated: `19654` + // Minimum execution time: 64_000 nanoseconds. + Weight::from_parts(65_000_000, 19654) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Control OrgCount (r:1 w:1) - // Storage: System Account (r:1 w:1) - // Storage: Control Orgs (r:1 w:1) - // Storage: Tokens Accounts (r:2 w:2) - // Storage: Control Members (r:1 w:1) - // Storage: Control OrgMemberCount (r:0 w:1) - // Storage: Control OrgTreasury (r:0 w:1) - // Storage: Control MemberStates (r:0 w:1) - // Storage: Control OrgStates (r:0 w:1) + /// Storage: Control OrgCount (r:1 w:1) + /// Proof: Control OrgCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Control Orgs (r:1 w:1) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Tokens Accounts (r:2 w:2) + /// Proof: Tokens Accounts (max_values: None, max_size: Some(109), added: 2584, mode: MaxEncodedLen) + /// Storage: Control Members (r:1 w:1) + /// Proof: Control Members (max_values: None, max_size: Some(32050), added: 34525, mode: MaxEncodedLen) + /// Storage: Control OrgMemberCount (r:0 w:1) + /// Proof: Control OrgMemberCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: Control OrgTreasury (r:0 w:1) + /// Proof: Control OrgTreasury (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Control MemberStates (r:0 w:1) + /// Proof: Control MemberStates (max_values: None, max_size: Some(97), added: 2572, mode: MaxEncodedLen) + /// Storage: Control OrgStates (r:0 w:1) + /// Proof: Control OrgStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn create_org() -> Weight { - (54_000_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(6 as Weight)) - .saturating_add(RocksDbWeight::get().writes(10 as Weight)) + // Proof Size summary in bytes: + // Measured: `377` + // Estimated: `50510` + // Minimum execution time: 82_000 nanoseconds. + Weight::from_parts(85_000_000, 50510) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(10_u64)) } - // Storage: Control Orgs (r:1 w:1) - // Storage: Control MemberStates (r:1 w:0) + /// Storage: Control Orgs (r:1 w:1) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Control MemberStates (r:1 w:0) + /// Proof: Control MemberStates (max_values: None, max_size: Some(97), added: 2572, mode: MaxEncodedLen) fn update_org() -> Weight { - (22_000_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + // Proof Size summary in bytes: + // Measured: `534` + // Estimated: `7317` + // Minimum execution time: 26_000 nanoseconds. + Weight::from_parts(27_000_000, 7317) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Control Orgs (r:1 w:0) - // Storage: Control OrgStates (r:0 w:1) + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Control OrgStates (r:0 w:1) + /// Proof: Control OrgStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn disable_org() -> Weight { - (18_000_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(1 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + // Proof Size summary in bytes: + // Measured: `449` + // Estimated: `3755` + // Minimum execution time: 19_000 nanoseconds. + Weight::from_parts(20_000_000, 3755) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: Control Orgs (r:1 w:0) - // Storage: Control OrgStates (r:0 w:1) + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Control OrgStates (r:0 w:1) + /// Proof: Control OrgStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn enable_org() -> Weight { - (18_000_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(1 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) - } - // Storage: Control Orgs (r:1 w:0) - // Storage: Control OrgTreasury (r:1 w:0) - // Storage: Control Members (r:1 w:1) - // Storage: Control OrgMemberCount (r:0 w:1) - // Storage: Control MemberStates (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `449` + // Estimated: `3755` + // Minimum execution time: 18_000 nanoseconds. + Weight::from_parts(19_000_000, 3755) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Control OrgTreasury (r:1 w:0) + /// Proof: Control OrgTreasury (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Control Members (r:1 w:1) + /// Proof: Control Members (max_values: None, max_size: Some(32050), added: 34525, mode: MaxEncodedLen) + /// Storage: Control OrgMemberCount (r:0 w:1) + /// Proof: Control OrgMemberCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: Control MemberStates (r:0 w:1) + /// Proof: Control MemberStates (max_values: None, max_size: Some(97), added: 2572, mode: MaxEncodedLen) + /// The range of component `r` is `[1, 999]`. fn add_member(r: u32, ) -> Weight { - (27_200_000 as Weight) - // Standard Error: 2_000 - .saturating_add((36_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(RocksDbWeight::get().reads(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes(3 as Weight)) - } - // Storage: Control Orgs (r:1 w:0) - // Storage: Control Members (r:1 w:1) - // Storage: Control OrgMemberCount (r:0 w:1) - // Storage: Control MemberStates (r:0 w:1) + // Proof Size summary in bytes: + // Measured: `597 + r * (32 ±0)` + // Estimated: `42815` + // Minimum execution time: 32_000 nanoseconds. + Weight::from_parts(31_011_478, 42815) + // Standard Error: 933 + .saturating_add(Weight::from_ref_time(36_876).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Control MemberStates (r:1 w:0) + /// Proof: Control MemberStates (max_values: None, max_size: Some(97), added: 2572, mode: MaxEncodedLen) + fn update_member_state() -> Weight { + // Proof Size summary in bytes: + // Measured: `573` + // Estimated: `7317` + // Minimum execution time: 16_000 nanoseconds. + Weight::from_parts(17_000_000, 7317) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + } + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Control Members (r:1 w:1) + /// Proof: Control Members (max_values: None, max_size: Some(32050), added: 34525, mode: MaxEncodedLen) + /// Storage: Control OrgMemberCount (r:0 w:1) + /// Proof: Control OrgMemberCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: Control MemberStates (r:0 w:1) + /// Proof: Control MemberStates (max_values: None, max_size: Some(97), added: 2572, mode: MaxEncodedLen) + /// The range of component `r` is `[1, 999]`. fn remove_member(r: u32, ) -> Weight { - (24_323_000 as Weight) - // Standard Error: 2_000 - .saturating_add((31_000 as Weight).saturating_mul(r as Weight)) - .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(3 as Weight)) - } - // Storage: Control Orgs (r:1 w:0) - // Storage: Control OrgTreasury (r:1 w:0) - // Storage: Tokens Accounts (r:2 w:2) - // Storage: System Account (r:2 w:1) + // Proof Size summary in bytes: + // Measured: `522 + r * (32 ±0)` + // Estimated: `39270` + // Minimum execution time: 28_000 nanoseconds. + Weight::from_parts(27_753_161, 39270) + // Standard Error: 784 + .saturating_add(Weight::from_ref_time(31_088).saturating_mul(r.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Control OrgTreasury (r:1 w:0) + /// Proof: Control OrgTreasury (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Tokens Accounts (r:2 w:2) + /// Proof: Tokens Accounts (max_values: None, max_size: Some(109), added: 2584, mode: MaxEncodedLen) + /// Storage: System Account (r:2 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) fn spend_funds() -> Weight { - (45_000_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(6 as Weight)) - .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + // Proof Size summary in bytes: + // Measured: `946` + // Estimated: `19654` + // Minimum execution time: 64_000 nanoseconds. + Weight::from_parts(65_000_000, 19654) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } -} +} \ No newline at end of file diff --git a/flow/Cargo.toml b/flow/Cargo.toml index c7ae09af7..fa2891bc8 100644 --- a/flow/Cargo.toml +++ b/flow/Cargo.toml @@ -1,11 +1,6 @@ -# ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -# ███░▄▄▄█░▄▄▀█░▄▀▄░█░▄▄█░▄▀█░▄▄▀█▀▄▄▀██ -# ███░█▄▀█░▀▀░█░█▄█░█░▄▄█░█░█░▀▀░█░██░██ -# ███▄▄▄▄█▄██▄█▄███▄█▄▄▄█▄▄██▄██▄██▄▄███ -# ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ [package] name = "gamedao-flow" -version = "1.2.0" +version = "1.3.0" authors = ["zero.io","gamedao.co"] edition = "2018" license = "GPL-3.0-or-later" @@ -13,37 +8,37 @@ description = "Simple Crowdfunding module, supporting multiple campaigns, which repository = "https://github.com/gamedaoco/gamedao-protocol" [dependencies] -serde = { version = "1.0.143", optional = true } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +serde = { version = "1.0.143", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28", default-features = false } -sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28", default-features = false } -frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28", default-features = false } -frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28", default-features = false } -frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28", default-features = false, optional = true } -sp-io = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.28", default-features=false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false } +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false, optional = true } +sp-io = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.43", default-features=false } -orml-traits = { path = "../../orml/traits", default-features = false } -orml-tokens = { path = "../../orml/tokens", optional = true } -orml-currencies = { path = "../../orml/currencies", optional = true } +orml-currencies = { git = "https://github.com/open-web3-stack/open-runtime-module-library", branch= "polkadot-v0.9.43", default-features = false } +orml-tokens = { git = "https://github.com/open-web3-stack/open-runtime-module-library", branch= "polkadot-v0.9.43", default-features = false } +orml-traits = { git = "https://github.com/open-web3-stack/open-runtime-module-library", branch= "polkadot-v0.9.43", default-features = false } gamedao-traits = { package = "gamedao-traits", path = "../traits", default-features = false } -gamedao-control = { package = "gamedao-control", path = "../control", optional = true } +gamedao-control = { package = "gamedao-control", path = "../control", default-features = false } [dev-dependencies] -sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28" } -frame-support-test = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28" } +sp-core = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.43" } -pallet-timestamp = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28" } -pallet-balances = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28" } +pallet-timestamp = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } +pallet-balances = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } gamedao-control = { package = "gamedao-control", path = "../control", default-features = true } [features] default = ["std"] runtime-benchmarks = [ - "frame-benchmarking", - "gamedao-traits/frame-benchmarking", + "frame-benchmarking/runtime-benchmarks", + "gamedao-traits/runtime-benchmarks", + "gamedao-control/runtime-benchmarks" ] std = [ "codec/std", @@ -54,9 +49,7 @@ std = [ "frame-system/std", "frame-benchmarking/std", - "sp-core/std", "sp-std/std", - "sp-runtime/std", "orml-traits/std", "orml-tokens/std", diff --git a/flow/src/benchmarking.rs b/flow/src/benchmarking.rs index 38db539cb..6cc4c7aa3 100644 --- a/flow/src/benchmarking.rs +++ b/flow/src/benchmarking.rs @@ -1,7 +1,7 @@ //! Benchmarking setup for gamedao-flow use super::*; - use crate::Pallet as Flow; + use frame_benchmarking::{account, benchmarks, impl_benchmark_test_suite, whitelisted_caller}; use frame_system::RawOrigin; use frame_support::traits::Hooks; @@ -42,7 +42,7 @@ benchmarks! { fund_account::(&caller)?; // Prepare organization and treasury - let org_id = T::Control::create_org(caller.clone().into())?; + let org_id = T::ControlBenchmarkHelper::create_org(caller.clone().into())?; let treasury_id = T::Control::org_treasury_account(&org_id).unwrap(); let bounded_vec = BoundedVec::truncate_from(vec![0; T::StringLimit::get() as usize]); fund_account::(&treasury_id)?; @@ -71,7 +71,7 @@ benchmarks! { fund_accounts::(&vec![owner.clone(), contributor.clone()])?; // Prepare organization and treasury - let org_id = T::Control::create_org(owner.clone().into())?; + let org_id = T::ControlBenchmarkHelper::create_org(owner.clone().into())?; let treasury_id = T::Control::org_treasury_account(&org_id).unwrap(); fund_account::(&treasury_id)?; @@ -94,7 +94,7 @@ benchmarks! { let now = frame_system::Pallet::::block_number(); let owner: T::AccountId = whitelisted_caller(); fund_account::(&owner)?; - let org_id = T::Control::create_org(owner.clone().into())?; + let org_id = T::ControlBenchmarkHelper::create_org(owner.clone().into())?; let treasury_id = T::Control::org_treasury_account(&org_id).unwrap(); fund_account::(&treasury_id)?; diff --git a/flow/src/lib.rs b/flow/src/lib.rs index ee42df7f0..7ee30fc8c 100644 --- a/flow/src/lib.rs +++ b/flow/src/lib.rs @@ -41,24 +41,23 @@ pub use types::{FlowProtocol, CampaignState, FlowGovernance, BlockType}; mod mock; mod tests; -mod migration; #[cfg(feature = "runtime-benchmarks")] mod benchmarking; pub mod weights; use frame_support::{ - dispatch::{DispatchResult, DispatchError, DispatchResultWithPostInfo}, - traits::{Get, BalanceStatus, Hooks, StorageVersion}, + dispatch::{DispatchResult, DispatchError}, + traits::{Get, BalanceStatus, Hooks}, weights::Weight, BoundedVec, log, transactional }; use scale_info::TypeInfo; use sp_runtime::{traits::{AtLeast32BitUnsigned, Hash}, Permill, ArithmeticError::Overflow}; +use sp_std::{vec::Vec, convert::{TryFrom, TryInto}}; -use sp_std::{vec, vec::Vec, convert::{TryFrom, TryInto}}; - - -use gamedao_traits::{ControlTrait, ControlBenchmarkingTrait, FlowTrait, FlowBenchmarkingTrait}; +#[cfg(feature = "runtime-benchmarks")] +use gamedao_traits::{ControlBenchmarkingTrait, FlowBenchmarkingTrait}; +use gamedao_traits::{ControlTrait, FlowTrait}; use orml_traits::{MultiCurrency, MultiReservableCurrency}; pub use pallet::*; @@ -77,19 +76,14 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; - /// The current storage version. - const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); - #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] - #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); #[pallet::config] pub trait Config: frame_system::Config { - type Event: From> - + IsType<::Event> - + Into<::Event>; + type RuntimeEvent: From> + + IsType<::RuntimeEvent> + + Into<::RuntimeEvent>; /// The units in which we record balances. type Balance: Member @@ -100,7 +94,7 @@ pub mod pallet { + MaybeSerializeDeserialize + MaxEncodedLen + TypeInfo; - + /// The currency ID type type CurrencyId: Member + Parameter @@ -108,7 +102,7 @@ pub mod pallet { + MaybeSerializeDeserialize + MaxEncodedLen + TypeInfo; - + /// Weight information for extrinsics in this module. type WeightInfo: WeightInfo; @@ -116,8 +110,10 @@ pub mod pallet { type Currency: MultiCurrency + MultiReservableCurrency; - type Control: ControlTrait - + ControlBenchmarkingTrait; + type Control: ControlTrait; + + #[cfg(feature = "runtime-benchmarks")] + type ControlBenchmarkHelper: ControlBenchmarkingTrait; /// The GameDAO Treasury AccountId. #[pallet::constant] @@ -126,7 +122,7 @@ pub mod pallet { /// The min length of a campaign name. #[pallet::constant] type MinNameLength: Get; - + /// The max number of campaigns per one block. #[pallet::constant] type MaxCampaignsPerBlock: Get; @@ -151,12 +147,12 @@ pub mod pallet { /// The CurrencyId which is used as a protokol token. #[pallet::constant] type ProtocolTokenId: Get; - + /// The CurrencyId which is used as a payment token. #[pallet::constant] type PaymentTokenId: Get; - /// The amount of comission to be paid from the Org treasury to GameDAO treasury + /// The amount of comission to be paid from the Org treasury to GameDAO treasury /// after successfull Campaign finalization #[pallet::constant] type CampaignFee: Get; @@ -171,27 +167,27 @@ pub mod pallet { } /// Campaign by its id. - /// + /// /// CampaignOf: map Hash => Campaign #[pallet::storage] pub(super) type CampaignOf = StorageMap<_, Blake2_128Concat, T::Hash, Campaign, OptionQuery>; /// Total number of campaigns. - /// + /// /// CampaignCount: u32 #[pallet::storage] #[pallet::getter(fn campaign_count)] pub type CampaignCount = StorageValue<_, u32, ValueQuery>; /// Total contributions balance per campaign. - /// + /// /// CampaignBalance: map Hash => Balance #[pallet::storage] pub(super) type CampaignBalance = StorageMap<_, Blake2_128Concat, T::Hash, T::Balance, ValueQuery>; /// Total contribution made by account id for particular campaign. /// campaign id, account id -> contribution. - /// + /// /// CampaignContribution: double map Hash, AccountId => Balance #[pallet::storage] pub(super) type CampaignContribution = @@ -199,14 +195,14 @@ pub mod pallet { /// Campaign state by campaign id. /// 0 created, 1 activated, 2 paused, ... - /// + /// /// CampaignStates: map Hash => CampaignState #[pallet::storage] pub(super) type CampaignStates = StorageMap<_, Blake2_128Concat, T::Hash, CampaignState, ValueQuery, GetDefault>; /// Campaigns starting/ending in block x. - /// + /// /// CampaignsByBlock: double map BlockType, BlockNumber => BoundedVec #[pallet::storage] pub(super) type CampaignsByBlock = @@ -219,14 +215,14 @@ pub mod pallet { /// Offset value - number of processed and sucessfully finalized contributions. /// Used during campaign finalization for processing contributors in batches. /// When MaxContributorsProcessing is achieved, set this offset to save the progress. - /// + /// /// ProcessingOffset: map Hash => u32 #[pallet::storage] pub(super) type ProcessingOffset = StorageMap<_, Blake2_128Concat, T::Hash, u32, ValueQuery>; /// Total number of contributors for particular campaign. This is needed for voting /// in order do determine eligible voters for Withdrawal proposal. - /// + /// /// CampaignContributors: map Hash => u64 #[pallet::storage] pub(super) type CampaignContributorsCount = StorageMap<_, Blake2_128Concat, T::Hash, u64, ValueQuery>; @@ -294,13 +290,13 @@ pub mod pallet { fn on_initialize(block_number: T::BlockNumber) -> Weight { // Activate campaigns - let campaigns = CampaignsByBlock::::get(BlockType::Start, &block_number); + let campaigns = CampaignsByBlock::::get(BlockType::Start, block_number); for campaign_id in &campaigns { - let campaign_state = CampaignStates::::get(&campaign_id); + let campaign_state = CampaignStates::::get(campaign_id); if campaign_state != CampaignState::Created { continue; // Just a safety check, never should happen }; - CampaignStates::::insert(&campaign_id, CampaignState::Active); + CampaignStates::::insert(campaign_id, CampaignState::Active); Self::deposit_event(Event::::Activated { campaign_id: *campaign_id }); } @@ -317,7 +313,7 @@ pub mod pallet { for (i, c) in contributors.clone().into_iter().enumerate() { if processed >= T::MaxContributorsProcessing::get() { let not_finalized = BoundedVec::truncate_from(contributors[i..].into()); - CampaignFinalizationQueue::::insert(&campaign_id, + CampaignFinalizationQueue::::insert(campaign_id, (&campaign, &campaign_balance, &campaign_state, &treasury_id, not_finalized)); contributors_finalized = false; break @@ -327,7 +323,7 @@ pub mod pallet { } if contributors_finalized { Self::finalize_campaign(&campaign_state, campaign_id, &campaign, campaign_balance, treasury_id, block_number); - CampaignFinalizationQueue::::remove(&campaign_id); + CampaignFinalizationQueue::::remove(campaign_id); } } T::WeightInfo::on_initialize(processed, campaigns.len() as u32) @@ -349,16 +345,16 @@ pub mod pallet { } let treasury_id = maybe_treasury_id.unwrap(); let campaign_balance = CampaignBalance::::get(campaign_id); - let contributors = CampaignContribution::::iter_key_prefix(&campaign_id).collect::>(); - let state: CampaignState; - if campaign_balance >= campaign.cap { - state = CampaignState::Succeeded; - } else { - state = CampaignState::Failed; - } + let contributors = CampaignContribution::::iter_key_prefix(campaign_id).collect::>(); + let state = + if campaign_balance >= campaign.cap { + CampaignState::Succeeded + } else { + CampaignState::Failed + }; if contributors.len() as u32 > T::MaxCampaignContributors::get() { log::error!( - target: "runtime::gamedao_flow", "MaxCampaignContributors exceeds limits {}, + target: "runtime::gamedao_flow", "MaxCampaignContributors exceeds limits {}, campaign '{:?}' haven't been scheduled for settlement", T::MaxCampaignContributors::get(), campaign_id, ); @@ -366,11 +362,7 @@ pub mod pallet { } let c = BoundedVec::try_from(contributors.clone()).unwrap(); CampaignFinalizationQueue::::insert(campaign_id, (campaign, campaign_balance, state, treasury_id, c)); - } - } - - fn on_runtime_upgrade() -> Weight { - migration::migrate::() + } } } @@ -391,9 +383,9 @@ pub mod pallet { /// - `token_symbol`: a new custom token symbol /// - `token_name`: a new custom token name /// - `start`: - /// - /// The two params `token_symbol` and `token_name` are meant for setting up a new custom token if creator wants to - /// conduct a token generation event. Therefore these two are optionals and would result in a TGE dropping + /// + /// The two params `token_symbol` and `token_name` are meant for setting up a new custom token if creator wants to + /// conduct a token generation event. Therefore these two are optionals and would result in a TGE dropping /// fungible token with a new currency id to contributors. /// /// Emits `CampaignCreated` event when successful. @@ -425,7 +417,7 @@ pub mod pallet { let min_deposit = T::MinCampaignDeposit::get().mul_floor(target); ensure!(deposit >= min_deposit, Error::::DepositInsufficient); ensure!(deposit <= target, Error::::DepositTooHigh); - + // Campaign start/expiry validation: let current_block = >::block_number(); let starts = start.unwrap_or(current_block); @@ -438,7 +430,7 @@ pub mod pallet { let index = CampaignCount::::get(); let campaign = types::Campaign { index, org_id, name: name.clone(), owner: creator.clone(), - admin: admin_id.clone(), deposit, start: starts, expiry, cap: target, + admin: admin_id.clone(), deposit, start: starts, expiry, cap: target, protocol, governance, cid, token_symbol, token_name, created: current_block, }; @@ -462,7 +454,7 @@ pub mod pallet { #[transactional] pub fn contribute(origin: OriginFor, campaign_id: T::Hash, contribution: T::Balance) -> DispatchResult { let sender = ensure_signed(origin)?; - let campaign = CampaignOf::::get(&campaign_id).ok_or(Error::::CampaignUnknown)?; + let campaign = CampaignOf::::get(campaign_id).ok_or(Error::::CampaignUnknown)?; let block_number = >::block_number(); ensure!(block_number < campaign.expiry, Error::::CampaignExpired); @@ -472,7 +464,7 @@ pub mod pallet { Error::::NoContributionsAllowed ); ensure!(contribution >= T::MinContribution::get(), Error::::ContributionInsufficient); - + Self::create_contribution(sender.clone(), campaign_id, contribution)?; Self::deposit_event(Event::Contributed { campaign_id, sender, @@ -487,52 +479,52 @@ pub mod pallet { impl Pallet { fn mint_campaign(campaign_id: &T::Hash, campaign: Campaign) -> DispatchResult { - let campaign_state; - if campaign.start > >::block_number() { - campaign_state = CampaignState::Created; - } else { - campaign_state = CampaignState::Active; - } - CampaignStates::::insert(&campaign_id, campaign_state); + let campaign_state = + if campaign.start > >::block_number() { + CampaignState::Created + } else { + CampaignState::Active + }; + CampaignStates::::insert(campaign_id, campaign_state); CampaignsByBlock::::try_mutate( BlockType::Start, campaign.start, |campaigns| -> Result<(), DispatchError> { - campaigns.try_push(campaign_id.clone()).map_err(|_| Error::::CampaignsPerBlockExceeded)?; + campaigns.try_push(*campaign_id).map_err(|_| Error::::CampaignsPerBlockExceeded)?; Ok(()) } )?; CampaignsByBlock::::try_mutate( BlockType::Expiry, campaign.expiry, |campaigns| -> Result<(), DispatchError> { - campaigns.try_push(campaign_id.clone()).map_err(|_| Error::::CampaignsPerBlockExceeded)?; + campaigns.try_push(*campaign_id).map_err(|_| Error::::CampaignsPerBlockExceeded)?; Ok(()) } )?; - CampaignOf::::insert(&campaign_id, campaign.clone()); + CampaignOf::::insert(campaign_id, campaign.clone()); CampaignCount::::set(campaign.index.checked_add(1).ok_or(Overflow)?); let treasury_id = T::Control::org_treasury_account(&campaign.org_id).ok_or(Error::::TreasuryNotExist)?; T::Currency::reserve( - T::ProtocolTokenId::get(), &treasury_id, campaign.deposit.clone() + T::ProtocolTokenId::get(), &treasury_id, campaign.deposit ).map_err(|_| Error::::TreasuryBalanceLow)?; Ok(()) } fn create_contribution(sender: T::AccountId, campaign_id: T::Hash, contribution: T::Balance) -> DispatchResult { - let is_returning_contributor = CampaignContribution::::contains_key(&campaign_id, &sender); + let is_returning_contributor = CampaignContribution::::contains_key(campaign_id, &sender); if !is_returning_contributor { - let contributors = CampaignContributorsCount::::get(&campaign_id); - CampaignContributorsCount::::insert(campaign_id.clone(), contributors.checked_add(1).ok_or(Overflow)?); + let contributors = CampaignContributorsCount::::get(campaign_id); + CampaignContributorsCount::::insert(campaign_id, contributors.checked_add(1).ok_or(Overflow)?); } // Reserve contributed amount T::Currency::reserve(T::PaymentTokenId::get(), &sender, contribution).map_err(|_| Error::::BalanceLow)?; // Update contributor balance for campaign - let total_contribution = CampaignContribution::::get(&campaign_id, &sender); - CampaignContribution::::insert(&campaign_id, &sender, total_contribution + contribution); + let total_contribution = CampaignContribution::::get(campaign_id, &sender); + CampaignContribution::::insert(campaign_id, &sender, total_contribution + contribution); // Update campaign balance - let total_campaign_balance = CampaignBalance::::get(&campaign_id); - CampaignBalance::::insert(&campaign_id, total_campaign_balance + contribution); + let total_campaign_balance = CampaignBalance::::get(campaign_id); + CampaignBalance::::insert(campaign_id, total_campaign_balance + contribution); Ok(()) } @@ -543,18 +535,24 @@ impl Pallet { campaign_id: T::Hash, org_treasury: &T::AccountId, ) { - if campaign_state == &CampaignState::Succeeded { - let contributor_balance = CampaignContribution::::get(campaign_id, &contributor); - let _transfer_amount = T::Currency::repatriate_reserved( - T::PaymentTokenId::get(), - &contributor, - &org_treasury, - contributor_balance.clone(), - BalanceStatus::Reserved - ); - } else if campaign_state == &CampaignState::Failed { - let contribution = CampaignContribution::::get(campaign_id, contributor.clone()); - T::Currency::unreserve(T::PaymentTokenId::get(), &contributor, contribution); + match *campaign_state { + CampaignState::Succeeded => { + let contributor_balance = CampaignContribution::::get(campaign_id, &contributor); + let _transfer_amount = T::Currency::repatriate_reserved( + T::PaymentTokenId::get(), + &contributor, + org_treasury, + contributor_balance, + BalanceStatus::Reserved + ); + }, + + CampaignState::Failed => { + let contribution = CampaignContribution::::get(campaign_id, contributor.clone()); + T::Currency::unreserve(T::PaymentTokenId::get(), &contributor, contribution); + }, + + _ => {}, } } @@ -566,30 +564,34 @@ impl Pallet { org_treasury: T::AccountId, block_number: T::BlockNumber, ) { - if campaign_state == &CampaignState::Succeeded { - let commission = T::CampaignFee::get().mul_floor(campaign_balance.clone()); - let _transfer_commission = T::Currency::repatriate_reserved( - T::PaymentTokenId::get(), - &org_treasury, - &T::GameDAOTreasury::get(), - commission, - BalanceStatus::Free - ); - // Update campaign balance - let updated_balance = campaign_balance - commission; - CampaignBalance::::insert(campaign_id, updated_balance); - CampaignStates::::insert(&campaign_id, CampaignState::Succeeded); - - Self::deposit_event(Event::Succeeded { campaign_id, campaign_balance: updated_balance, block_number }); - - } else if campaign_state == &CampaignState::Failed { - // Unreserve Initial deposit - T::Currency::unreserve(T::ProtocolTokenId::get(), &org_treasury, campaign.deposit); - CampaignStates::::insert(campaign_id, CampaignState::Failed); - - Self::deposit_event(Event::Failed { campaign_id, campaign_balance, block_number }); + match *campaign_state { + CampaignState::Succeeded => { + let commission = T::CampaignFee::get().mul_floor(campaign_balance); + let _transfer_commission = T::Currency::repatriate_reserved( + T::PaymentTokenId::get(), + &org_treasury, + &T::GameDAOTreasury::get(), + commission, + BalanceStatus::Free + ); + // Update campaign balance + let updated_balance = campaign_balance - commission; + CampaignBalance::::insert(campaign_id, updated_balance); + CampaignStates::::insert(campaign_id, CampaignState::Succeeded); + + Self::deposit_event(Event::Succeeded { campaign_id, campaign_balance: updated_balance, block_number }); + }, + + CampaignState::Failed => { + // Unreserve Initial deposit + T::Currency::unreserve(T::ProtocolTokenId::get(), &org_treasury, campaign.deposit); + CampaignStates::::insert(campaign_id, CampaignState::Failed); + + Self::deposit_event(Event::Failed { campaign_id, campaign_balance, block_number }); + }, + + _ => {}, } - } } @@ -604,9 +606,10 @@ impl FlowTrait for Pallet { fn campaign_owner(campaign_id: &T::Hash) -> Option { let campaign = CampaignOf::::get(campaign_id); if let Some(campaign) = campaign { - return Some(campaign.owner); - }; - return None; + Some(campaign.owner) + } else { + None + } } fn is_campaign_succeeded(campaign_id: &T::Hash) -> bool { CampaignStates::::get(campaign_id) == CampaignState::Succeeded @@ -616,12 +619,12 @@ impl FlowTrait for Pallet { } } +#[cfg(feature = "runtime-benchmarks")] impl FlowBenchmarkingTrait for Pallet { - /// ** Should be used for benchmarking only!!! ** - #[cfg(feature = "runtime-benchmarks")] fn create_campaign(caller: &T::AccountId, org_id: &T::Hash, start: T::BlockNumber) -> Result { use sp_runtime::traits::Saturating; + use sp_std::vec; let bounded_str: BoundedVec = BoundedVec::truncate_from(vec![0; T::StringLimit::get() as usize]); let now = frame_system::Pallet::::block_number(); let index = CampaignCount::::get(); @@ -635,7 +638,7 @@ impl FlowBenchmarkingTrait for deposit: T::MinContribution::get(), start, expiry: start + 57_600_u32.into(), // 60/3*60*24*2 (2 days with 3 sec block time) - cap: target, + cap: target, protocol: FlowProtocol::default(), governance: FlowGovernance::default(), cid: bounded_str.clone(), @@ -654,8 +657,6 @@ impl FlowBenchmarkingTrait for Ok(campaign_id) } - /// ** Should be used for benchmarking only!!! ** - #[cfg(feature = "runtime-benchmarks")] fn create_contributions(campaign_id: &T::Hash, contributors: Vec) -> Result<(), DispatchError> { for account_id in BoundedVec::::truncate_from(contributors) { Pallet::::contribute( @@ -667,8 +668,6 @@ impl FlowBenchmarkingTrait for Ok(()) } - /// ** Should be used for benchmarking only!!! ** - #[cfg(feature = "runtime-benchmarks")] fn finalize_campaigns_by_block(block_number: T::BlockNumber) { use sp_runtime::traits::Saturating; frame_system::Pallet::::set_block_number(block_number); diff --git a/flow/src/migration.rs b/flow/src/migration.rs deleted file mode 100644 index 999a05c1d..000000000 --- a/flow/src/migration.rs +++ /dev/null @@ -1,41 +0,0 @@ -use frame_support::{ - traits::{Get, GetStorageVersion, PalletInfoAccess, StorageVersion}, - Blake2_128Concat, - BoundedVec -}; -use sp_std::prelude::*; -use crate::{ - // CampaignsByState as CampaignsByStateNew, - // CampaignOrg, - Config, - // FlowState, - Pallet, - Weight -}; - - -pub fn migrate() -> Weight { - - let version = StorageVersion::get::>(); - let mut weight: Weight = 0; - - if version < 2 { - weight = weight.saturating_add(v2::migrate::()); - StorageVersion::new(2).put::>(); - } - - weight -} - -mod v2 { - use super::*; - use sp_io::hashing::twox_128; - - pub fn migrate() -> Weight { - let _ = frame_support::storage::unhashed::clear_prefix( - &twox_128(>::name().as_bytes()), None, None - ); - - T::DbWeight::get().writes(1) - } -} diff --git a/flow/src/mock.rs b/flow/src/mock.rs index 6a410a03e..e4514b15d 100644 --- a/flow/src/mock.rs +++ b/flow/src/mock.rs @@ -1,6 +1,5 @@ #![cfg(test)] -use super::{FlowProtocol, FlowGovernance}; use sp_std::{vec, vec::Vec, convert::{TryFrom, TryInto}}; use frame_support::{ construct_runtime, parameter_types, PalletId, @@ -53,7 +52,6 @@ pub const ALICE: AccountId = 11; pub const BOB: AccountId = 12; pub const GAMEDAO_TREASURY: AccountId = 13; -pub const GAME3_TREASURY: AccountId = 14; pub const INIT_BALANCE: Balance = 100 * DOLLARS; @@ -66,16 +64,16 @@ parameter_types! { } impl frame_system::Config for Test { - type Origin = Origin; + type RuntimeOrigin = RuntimeOrigin; type Index = u64; type BlockNumber = BlockNumber; - type Call = Call; + type RuntimeCall = RuntimeCall; type Hash = Hash; type Hashing = BlakeTwo256; type AccountId = AccountId; type Lookup = IdentityLookup; type Header = sp_runtime::testing::Header; - type Event = Event; + type RuntimeEvent = RuntimeEvent; type BlockHashCount = BlockHashCount; type BlockWeights = (); type BlockLength = (); @@ -102,19 +100,17 @@ parameter_types! { } impl orml_tokens::Config for Test { - type Event = Event; + type RuntimeEvent = RuntimeEvent; type Balance = Balance; type Amount = Amount; type CurrencyId = CurrencyId; type WeightInfo = (); type ExistentialDeposits = ExistentialDeposits; - type OnDust = (); + type CurrencyHooks = (); type MaxLocks = (); type MaxReserves = MaxReserves; - type OnNewTokenAccount = (); - type OnKilledTokenAccount = (); - type DustRemovalWhitelist = Nothing; type ReserveIdentifier = ReserveIdentifier; + type DustRemovalWhitelist = Nothing; } parameter_types! { @@ -124,7 +120,7 @@ parameter_types! { impl pallet_balances::Config for Test { type Balance = Balance; type DustRemoval = (); - type Event = Event; + type RuntimeEvent = RuntimeEvent; type ExistentialDeposit = ExistentialDeposit; type AccountStore = frame_system::Pallet; type MaxLocks = (); @@ -151,7 +147,7 @@ impl gamedao_control::Config for Test { type Balance = Balance; type CurrencyId = CurrencyId; type WeightInfo = (); - type Event = Event; + type RuntimeEvent = RuntimeEvent; type Currency = Currencies; type MaxMembers = ConstU32<10000>; type ProtocolTokenId = ProtocolTokenId; @@ -166,19 +162,21 @@ parameter_types! { pub const MaxCampaignsPerBlock: u32 = 2; pub const MaxContributorsProcessing: u32 = 4; pub const MinContribution: Balance = 1 * DOLLARS; - pub CampaignFee: Permill = Permill::from_rational(1u32, 10u32); // 10% + pub CampaignFee: Permill = Permill::from_rational(1u32, 15u32); // 15% pub const GameDAOTreasury: AccountId = GAMEDAO_TREASURY; pub const CampaignDurationLimits: (BlockNumber, BlockNumber) = (1 * DAYS, 100 * DAYS); pub MinCampaignDeposit: Permill = Permill::from_rational(1u32, 10u32); // 10% } impl gamedao_flow::Config for Test { - type Event = Event; + type RuntimeEvent = RuntimeEvent; type Balance = Balance; type CurrencyId = CurrencyId; type WeightInfo = (); type Currency = Currencies; type Control = Control; + #[cfg(feature = "runtime-benchmarks")] + type ControlBenchmarkHelper = Control; type GameDAOTreasury = GameDAOTreasury; type MinNameLength = MinNameLength; type MaxCampaignsPerBlock = MaxCampaignsPerBlock; diff --git a/flow/src/tests.rs b/flow/src/tests.rs index 2e77030d3..71b7f6863 100644 --- a/flow/src/tests.rs +++ b/flow/src/tests.rs @@ -4,14 +4,14 @@ use frame_support::traits::Hooks; use frame_support::{assert_noop, assert_ok}; use frame_system::RawOrigin; use sp_core::H256; -use sp_runtime::traits::{Hash, AccountIdConversion}; +use sp_runtime::traits::Hash; use gamedao_control::types::{AccessModel, FeeModel, OrgType, Org}; use super::{ types::{FlowProtocol, FlowGovernance}, mock::{ - BlockNumber, AccountId, Balance, Control, Event, Tokens, INIT_BALANCE, - Flow, Origin, System, Test, ALICE, BOB, DOLLARS, DAYS, new_test_ext, + BlockNumber, AccountId, Balance, Control, RuntimeEvent as Event, Tokens, INIT_BALANCE, + Flow, RuntimeOrigin as Origin, System, Test, ALICE, BOB, DOLLARS, DAYS, new_test_ext, PROTOCOL_TOKEN_ID, PAYMENT_TOKEN_ID, CampaignDurationLimits, MaxContributorsProcessing, }, * @@ -53,7 +53,7 @@ pub fn create_campaign( deposit, start, expiry, - cap: target, + cap: target, protocol: FlowProtocol::default(), governance: FlowGovernance::default(), cid: bounded_str.clone(), @@ -152,7 +152,7 @@ fn flow_create_errors() { ); // Ensure campaign expires before expiration limit // Error: OutOfBounds - let (min_duration, max_duration) = CampaignDurationLimits::get(); + let (_min_duration, max_duration) = CampaignDurationLimits::get(); let expiration_block = max_duration + now + 1; assert_noop!( Flow::create_campaign( @@ -301,7 +301,7 @@ fn flow_contribute_success() { } /// Tests queue when two campaigns created -// #[test] +#[test] fn flow_on_finalize_campaign_succeess() { new_test_ext().execute_with(|| { let (org_id, treasury_id, tbalance) = create_org_treasury(); @@ -324,7 +324,7 @@ fn flow_on_finalize_campaign_succeess() { campaign_rev.deposit, campaign_rev.expiry, campaign_rev.protocol.clone(), campaign_rev.governance.clone(), campaign_rev.cid.clone(), None, None, None )); - + // Contribute (10/500) assert_ok!(Flow::contribute(Origin::signed(1), campaign_id_rev, 10 * DOLLARS)); @@ -362,40 +362,41 @@ fn flow_on_finalize_campaign_succeess() { let batch_size: u128 = 4; assert_eq!(MaxContributorsProcessing::get(), batch_size as u32); - // --------- Block 1: process first 4 contributors --------- + // --------- Block 1: process 1 contributor from Campaign1 and revert Campaign1, process 3 contributors from Campaign2 --------- System::set_block_number(expiry + 1); Flow::on_initialize(expiry + 1); + // Campaign1 failed with 1 contributor, so we don't count his contribution assert_eq!( ::Currency::total_balance(PAYMENT_TOKEN_ID, &treasury_id), - batch_size * contribution + batch_size * contribution - contribution ); assert_eq!( ::Currency::free_balance(PAYMENT_TOKEN_ID, &treasury_id), 0 ); - // --------- Block 2: process next 4 contributors --------- + // --------- Block 2: process next 4 contributors from Campaign2 --------- System::set_block_number(expiry + 2); Flow::on_initialize(expiry + 2); assert_eq!( ::Currency::total_balance(PAYMENT_TOKEN_ID, &treasury_id), - 2 * batch_size * contribution + 2 * batch_size * contribution - contribution ); assert_eq!( ::Currency::free_balance(PAYMENT_TOKEN_ID, &treasury_id), 0 ); assert_eq!(CampaignStates::::get(&campaign_id), CampaignState::Active); - assert_eq!(CampaignStates::::get(&campaign_id_rev), CampaignState::Active); + assert_eq!(CampaignStates::::get(&campaign_id_rev), CampaignState::Failed); - // --------- Block 3: process last 2 contributors and finalize Campaign1, process 1 contributor and revert Campaign2 --------- + // --------- Block 3: process last 3 contributors from Campaign2 and finalize Campaign2 --------- System::set_block_number(expiry + 3); Flow::on_initialize(expiry + 3); // Campaign finalized: - let commission = ::CampaignFee::get().mul_floor(contribution * 10); + let commission = ::CampaignFee::get().mul_floor(contribution * total_contributors); // The balance was transfered and locked in the org treasury assert_eq!( ::Currency::total_balance(PAYMENT_TOKEN_ID, &treasury_id), @@ -420,22 +421,27 @@ fn flow_on_finalize_campaign_succeess() { assert_eq!(CampaignStates::::get(&campaign_id_rev), CampaignState::Failed); // Last event: - let events = System::events().into_iter().map(|evt| evt.event).collect::>(); + let events = System::events() + .into_iter() + .map(|evt| evt.event) + .filter_map(|e| if let Event::Flow(inner) = e { Some(inner) } else { None }) + .collect::>(); + assert_eq!( - events[events.len() - 4], - Event::Flow(crate::Event::Succeeded { - campaign_id, - campaign_balance: CampaignBalance::::get(campaign_id), - block_number: expiry + 3, - }) + events[events.len() - 2], + crate::Event::Failed { + campaign_id: campaign_id_rev, + campaign_balance: CampaignBalance::::get(campaign_id_rev), + block_number: expiry + 1, + } ); assert_eq!( events[events.len() - 1], - Event::Flow(crate::Event::Failed { - campaign_id: campaign_id_rev, - campaign_balance: CampaignBalance::::get(campaign_id_rev), + crate::Event::Succeeded { + campaign_id, + campaign_balance: CampaignBalance::::get(campaign_id), block_number: expiry + 3, - }) + } ); }); @@ -464,7 +470,7 @@ fn flow_on_finalize_campaign_failed() { campaign.cid.clone(), None, None, None )); - let mut contributors: Vec = (1..11).collect(); + let contributors: Vec = (1..11).collect(); // Contribute (600/1000) for c in &contributors { assert_ok!(Flow::contribute(Origin::signed(*c), campaign_id, contribution)); @@ -483,7 +489,7 @@ fn flow_on_finalize_campaign_failed() { System::set_block_number(expiry + 1); Flow::on_initialize(expiry + 1); - // Account's balance from the first batch was unlocked + // Account's balance from the first batch was unlocked assert_eq!(::Currency::free_balance(PAYMENT_TOKEN_ID, &c[0]), INIT_BALANCE); assert_eq!(::Currency::free_balance(PAYMENT_TOKEN_ID, &c[1]), INIT_BALANCE); assert_eq!(::Currency::free_balance(PAYMENT_TOKEN_ID, &c[2]), INIT_BALANCE); diff --git a/flow/src/weights.rs b/flow/src/weights.rs index 65f6db6ad..13e13fa32 100644 --- a/flow/src/weights.rs +++ b/flow/src/weights.rs @@ -1,35 +1,21 @@ -// This file is part of Substrate. - -// Copyright (C) 2021 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. //! Autogenerated weights for gamedao_flow //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-08-12, STEPS: `20`, REPEAT: 10, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 +//! DATE: 2023-04-19, STEPS: `20`, REPEAT: `10`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 // Executed Command: // ./target/release/subzero // benchmark // pallet +// --execution=wasm // --pallet=gamedao_flow // --extrinsic=* // --steps=20 // --repeat=10 -// --output=gamedao-protocol/flow/src/weights.rs +// --output=modules/gamedao-protocol/flow/src/weights.rs // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -49,94 +35,166 @@ pub trait WeightInfo { /// Weights for gamedao_flow using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Control OrgController (r:1 w:0) - // Storage: Flow CampaignCount (r:1 w:1) - // Storage: Flow CampaignsByBlock (r:2 w:2) - // Storage: Control OrgTreasury (r:1 w:0) - // Storage: Tokens Accounts (r:1 w:1) - // Storage: Flow CampaignOf (r:0 w:1) - // Storage: Flow CampaignStates (r:0 w:1) + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Flow CampaignCount (r:1 w:1) + /// Proof: Flow CampaignCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Flow CampaignsByBlock (r:2 w:2) + /// Proof: Flow CampaignsByBlock (max_values: None, max_size: Some(358), added: 2833, mode: MaxEncodedLen) + /// Storage: Control OrgTreasury (r:1 w:0) + /// Proof: Control OrgTreasury (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Tokens Accounts (r:1 w:1) + /// Proof: Tokens Accounts (max_values: None, max_size: Some(109), added: 2584, mode: MaxEncodedLen) + /// Storage: Flow CampaignOf (r:0 w:1) + /// Proof: Flow CampaignOf (max_values: None, max_size: Some(460), added: 2935, mode: MaxEncodedLen) + /// Storage: Flow CampaignStates (r:0 w:1) + /// Proof: Flow CampaignStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn create_campaign() -> Weight { - (41_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(6 as Weight)) - .saturating_add(T::DbWeight::get().writes(6 as Weight)) + // Proof Size summary in bytes: + // Measured: `947` + // Estimated: `19019` + // Minimum execution time: 66_000 nanoseconds. + Weight::from_parts(68_000_000, 19019) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) } - // Storage: Flow CampaignOf (r:1 w:0) - // Storage: Flow CampaignStates (r:1 w:0) - // Storage: Flow CampaignContribution (r:1 w:1) - // Storage: Flow CampaignContributorsCount (r:1 w:1) - // Storage: Tokens Accounts (r:1 w:1) - // Storage: Flow CampaignBalance (r:1 w:1) + /// Storage: Flow CampaignOf (r:1 w:0) + /// Proof: Flow CampaignOf (max_values: None, max_size: Some(460), added: 2935, mode: MaxEncodedLen) + /// Storage: Flow CampaignStates (r:1 w:0) + /// Proof: Flow CampaignStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Flow CampaignContribution (r:1 w:1) + /// Proof: Flow CampaignContribution (max_values: None, max_size: Some(112), added: 2587, mode: MaxEncodedLen) + /// Storage: Flow CampaignContributorsCount (r:1 w:1) + /// Proof: Flow CampaignContributorsCount (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen) + /// Storage: Tokens Accounts (r:1 w:1) + /// Proof: Tokens Accounts (max_values: None, max_size: Some(109), added: 2584, mode: MaxEncodedLen) + /// Storage: Flow CampaignBalance (r:1 w:1) + /// Proof: Flow CampaignBalance (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) fn contribute() -> Weight { - (37_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(6 as Weight)) - .saturating_add(T::DbWeight::get().writes(4 as Weight)) + // Proof Size summary in bytes: + // Measured: `1042` + // Estimated: `21640` + // Minimum execution time: 53_000 nanoseconds. + Weight::from_parts(54_000_000, 21640) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)) } - // Storage: Flow CampaignsByBlock (r:1 w:0) - // Storage: Flow CampaignStates (r:10 w:11) - // Storage: Flow CampaignFinalizationQueue (r:2 w:1) - // Storage: Tokens Accounts (r:1 w:1) - // Storage: Flow CampaignContribution (r:5 w:0) - // Storage: System Account (r:1 w:1) - // Storage: Flow CampaignBalance (r:0 w:1) + /// Storage: Flow CampaignsByBlock (r:1 w:0) + /// Proof: Flow CampaignsByBlock (max_values: None, max_size: Some(358), added: 2833, mode: MaxEncodedLen) + /// Storage: Flow CampaignStates (r:10 w:11) + /// Proof: Flow CampaignStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Flow CampaignFinalizationQueue (r:2 w:1) + /// Proof: Flow CampaignFinalizationQueue (max_values: None, max_size: Some(320511), added: 322986, mode: MaxEncodedLen) + /// Storage: Flow CampaignContribution (r:100 w:0) + /// Proof: Flow CampaignContribution (max_values: None, max_size: Some(112), added: 2587, mode: MaxEncodedLen) + /// Storage: Tokens Accounts (r:102 w:102) + /// Proof: Tokens Accounts (max_values: None, max_size: Some(109), added: 2584, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Flow CampaignBalance (r:0 w:1) + /// Proof: Flow CampaignBalance (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) + /// The range of component `c` is `[0, 100]`. + /// The range of component `p` is `[0, 10]`. fn on_initialize(c: u32, p: u32, ) -> Weight { - (32_693_000 as Weight) - // Standard Error: 51_000 - .saturating_add((15_609_000 as Weight).saturating_mul(c as Weight)) - // Standard Error: 585_000 - .saturating_add((6_409_000 as Weight).saturating_mul(p as Weight)) - .saturating_add(T::DbWeight::get().reads(5 as Weight)) - .saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(c as Weight))) - .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(p as Weight))) - .saturating_add(T::DbWeight::get().writes(5 as Weight)) - .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(c as Weight))) - .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(p as Weight))) + // Proof Size summary in bytes: + // Measured: `1489 + c * (257 ±0) + p * (87 ±0)` + // Estimated: `660937 + c * (5183 ±0) + p * (2595 ±4)` + // Minimum execution time: 146_000 nanoseconds. + Weight::from_parts(68_171_926, 660937) + // Standard Error: 65_182 + .saturating_add(Weight::from_ref_time(29_183_288).saturating_mul(c.into())) + // Standard Error: 651_727 + .saturating_add(Weight::from_ref_time(11_547_431).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(5_u64)) + .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(c.into()))) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(p.into()))) + .saturating_add(T::DbWeight::get().writes(5_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(c.into()))) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into()))) + .saturating_add(Weight::from_proof_size(5183).saturating_mul(c.into())) + .saturating_add(Weight::from_proof_size(2595).saturating_mul(p.into())) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Control OrgController (r:1 w:0) - // Storage: Flow CampaignCount (r:1 w:1) - // Storage: Flow CampaignsByBlock (r:2 w:2) - // Storage: Control OrgTreasury (r:1 w:0) - // Storage: Tokens Accounts (r:1 w:1) - // Storage: Flow CampaignOf (r:0 w:1) - // Storage: Flow CampaignStates (r:0 w:1) + /// Storage: Control Orgs (r:1 w:0) + /// Proof: Control Orgs (max_values: None, max_size: Some(290), added: 2765, mode: MaxEncodedLen) + /// Storage: Flow CampaignCount (r:1 w:1) + /// Proof: Flow CampaignCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Flow CampaignsByBlock (r:2 w:2) + /// Proof: Flow CampaignsByBlock (max_values: None, max_size: Some(358), added: 2833, mode: MaxEncodedLen) + /// Storage: Control OrgTreasury (r:1 w:0) + /// Proof: Control OrgTreasury (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Tokens Accounts (r:1 w:1) + /// Proof: Tokens Accounts (max_values: None, max_size: Some(109), added: 2584, mode: MaxEncodedLen) + /// Storage: Flow CampaignOf (r:0 w:1) + /// Proof: Flow CampaignOf (max_values: None, max_size: Some(460), added: 2935, mode: MaxEncodedLen) + /// Storage: Flow CampaignStates (r:0 w:1) + /// Proof: Flow CampaignStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn create_campaign() -> Weight { - (41_000_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(6 as Weight)) - .saturating_add(RocksDbWeight::get().writes(6 as Weight)) + // Proof Size summary in bytes: + // Measured: `947` + // Estimated: `19019` + // Minimum execution time: 66_000 nanoseconds. + Weight::from_parts(68_000_000, 19019) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) } - // Storage: Flow CampaignOf (r:1 w:0) - // Storage: Flow CampaignStates (r:1 w:0) - // Storage: Flow CampaignContribution (r:1 w:1) - // Storage: Flow CampaignContributorsCount (r:1 w:1) - // Storage: Tokens Accounts (r:1 w:1) - // Storage: Flow CampaignBalance (r:1 w:1) + /// Storage: Flow CampaignOf (r:1 w:0) + /// Proof: Flow CampaignOf (max_values: None, max_size: Some(460), added: 2935, mode: MaxEncodedLen) + /// Storage: Flow CampaignStates (r:1 w:0) + /// Proof: Flow CampaignStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Flow CampaignContribution (r:1 w:1) + /// Proof: Flow CampaignContribution (max_values: None, max_size: Some(112), added: 2587, mode: MaxEncodedLen) + /// Storage: Flow CampaignContributorsCount (r:1 w:1) + /// Proof: Flow CampaignContributorsCount (max_values: None, max_size: Some(56), added: 2531, mode: MaxEncodedLen) + /// Storage: Tokens Accounts (r:1 w:1) + /// Proof: Tokens Accounts (max_values: None, max_size: Some(109), added: 2584, mode: MaxEncodedLen) + /// Storage: Flow CampaignBalance (r:1 w:1) + /// Proof: Flow CampaignBalance (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) fn contribute() -> Weight { - (37_000_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(6 as Weight)) - .saturating_add(RocksDbWeight::get().writes(4 as Weight)) + // Proof Size summary in bytes: + // Measured: `1042` + // Estimated: `21640` + // Minimum execution time: 53_000 nanoseconds. + Weight::from_parts(54_000_000, 21640) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(4_u64)) } - // Storage: Flow CampaignsByBlock (r:1 w:0) - // Storage: Flow CampaignStates (r:10 w:11) - // Storage: Flow CampaignFinalizationQueue (r:2 w:1) - // Storage: Tokens Accounts (r:1 w:1) - // Storage: Flow CampaignContribution (r:5 w:0) - // Storage: System Account (r:1 w:1) - // Storage: Flow CampaignBalance (r:0 w:1) + /// Storage: Flow CampaignsByBlock (r:1 w:0) + /// Proof: Flow CampaignsByBlock (max_values: None, max_size: Some(358), added: 2833, mode: MaxEncodedLen) + /// Storage: Flow CampaignStates (r:10 w:11) + /// Proof: Flow CampaignStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Flow CampaignFinalizationQueue (r:2 w:1) + /// Proof: Flow CampaignFinalizationQueue (max_values: None, max_size: Some(320511), added: 322986, mode: MaxEncodedLen) + /// Storage: Flow CampaignContribution (r:100 w:0) + /// Proof: Flow CampaignContribution (max_values: None, max_size: Some(112), added: 2587, mode: MaxEncodedLen) + /// Storage: Tokens Accounts (r:102 w:102) + /// Proof: Tokens Accounts (max_values: None, max_size: Some(109), added: 2584, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: Flow CampaignBalance (r:0 w:1) + /// Proof: Flow CampaignBalance (max_values: None, max_size: Some(64), added: 2539, mode: MaxEncodedLen) + /// The range of component `c` is `[0, 100]`. + /// The range of component `p` is `[0, 10]`. fn on_initialize(c: u32, p: u32, ) -> Weight { - (32_693_000 as Weight) - // Standard Error: 51_000 - .saturating_add((15_609_000 as Weight).saturating_mul(c as Weight)) - // Standard Error: 585_000 - .saturating_add((6_409_000 as Weight).saturating_mul(p as Weight)) - .saturating_add(RocksDbWeight::get().reads(5 as Weight)) - .saturating_add(RocksDbWeight::get().reads((2 as Weight).saturating_mul(c as Weight))) - .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(p as Weight))) - .saturating_add(RocksDbWeight::get().writes(5 as Weight)) - .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(c as Weight))) - .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(p as Weight))) + // Proof Size summary in bytes: + // Measured: `1489 + c * (257 ±0) + p * (87 ±0)` + // Estimated: `660937 + c * (5183 ±0) + p * (2595 ±4)` + // Minimum execution time: 146_000 nanoseconds. + Weight::from_parts(68_171_926, 660937) + // Standard Error: 65_182 + .saturating_add(Weight::from_ref_time(29_183_288).saturating_mul(c.into())) + // Standard Error: 651_727 + .saturating_add(Weight::from_ref_time(11_547_431).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(5_u64)) + .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(c.into()))) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(p.into()))) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(c.into()))) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(p.into()))) + .saturating_add(Weight::from_proof_size(5183).saturating_mul(c.into())) + .saturating_add(Weight::from_proof_size(2595).saturating_mul(p.into())) } -} +} \ No newline at end of file diff --git a/sense/Cargo.toml b/sense/Cargo.toml index cdc833795..e9e70eb8e 100755 --- a/sense/Cargo.toml +++ b/sense/Cargo.toml @@ -1,11 +1,6 @@ -# ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -# ███░▄▄▄█░▄▄▀█░▄▀▄░█░▄▄█░▄▀█░▄▄▀█▀▄▄▀██ -# ███░█▄▀█░▀▀░█░█▄█░█░▄▄█░█░█░▀▀░█░██░██ -# ███▄▄▄▄█▄██▄█▄███▄█▄▄▄█▄▄██▄██▄██▄▄███ -# ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ [package] name = "gamedao-sense" -version = "1.2.0" +version = "1.3.0" authors = ["zero.io","gamedao.co"] edition = "2018" license = "GPL-3.0-or-later" @@ -19,19 +14,19 @@ categories = [ ] [dependencies] -serde = { version = "1.0.143", optional = true } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +serde = { version = "1.0.143", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28", default-features = false } -sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28", default-features = false } -frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28", default-features = false } -frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28", default-features = false } -frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28", default-features = false, optional = true } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false } +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false, optional = true } [dev-dependencies] -sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28" } -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28" } -sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28" } +sp-io = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.43" } +sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.43" } +sp-core = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.43" } [features] default = ["std"] @@ -45,8 +40,6 @@ std = [ "frame-system/std", "frame-benchmarking/std", - "sp-core/std", "sp-std/std", - "sp-runtime/std", ] try-runtime = ["frame-support/try-runtime"] diff --git a/sense/src/benchmarking.rs b/sense/src/benchmarking.rs index 7aacf493b..dc3fedcba 100644 --- a/sense/src/benchmarking.rs +++ b/sense/src/benchmarking.rs @@ -12,10 +12,10 @@ benchmarks! { create_entity {}: _(RawOrigin::Root, account("1", 0, 0), BoundedVec::truncate_from(vec![1; 256])) update_property { - let caller_origin = ::Origin::from(RawOrigin::Root); + let caller_origin = ::RuntimeOrigin::from(RawOrigin::Root); let property_type = PropertyType::Experience; Sense::::create_entity(caller_origin, account("1", 0, 0), BoundedVec::truncate_from(vec![1; 1]))?; }: _(RawOrigin::Root, account("1", 0, 0), property_type, 255) -} -impl_benchmark_test_suite!(Sense, crate::mock::new_test_ext(), crate::mock::Test); + impl_benchmark_test_suite!(Sense, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/sense/src/lib.rs b/sense/src/lib.rs index 0e0a50a99..f2bc6f80b 100644 --- a/sense/src/lib.rs +++ b/sense/src/lib.rs @@ -33,9 +33,9 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { - type Event: From> - + IsType<::Event> - + Into<::Event>; + type RuntimeEvent: From> + + IsType<::RuntimeEvent> + + Into<::RuntimeEvent>; type WeightInfo: WeightInfo; /// The maximum length of a name or symbol stored on-chain. @@ -44,37 +44,36 @@ pub mod pallet { } #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); /// Sense Entity of the account. - /// + /// /// Entities: map AccountId => Entity #[pallet::storage] #[pallet::getter(fn get_entity)] - pub(super) type Entities = StorageMap<_, - Blake2_128Concat, + pub(super) type Entities = StorageMap<_, + Blake2_128Concat, T::AccountId, - Entity>, + Entity>, OptionQuery >; /// EntityCount. Increase per each entity creation. - /// + /// /// EntityCount: u128 #[pallet::storage] #[pallet::getter(fn get_entity_count)] pub type EntityCount = StorageValue<_, u128, ValueQuery>; /// All properties of the account. - /// + /// /// Properties: map (PropertyType, AccountId) => EntityProperty #[pallet::storage] #[pallet::getter(fn get_property)] - pub(super) type Properties = StorageDoubleMap<_, + pub(super) type Properties = StorageDoubleMap<_, Blake2_128Concat, PropertyType, - Blake2_128Concat, T::AccountId, - EntityProperty, + Blake2_128Concat, T::AccountId, + EntityProperty, OptionQuery >; @@ -83,13 +82,13 @@ pub mod pallet { pub enum Event { /// New Sense Entity was created. EntityCreated{ - account_id: T::AccountId, + account_id: T::AccountId, block_number: T::BlockNumber }, /// Property was updated. PropertyUpdated{ property_type: PropertyType, - account_id: T::AccountId, + account_id: T::AccountId, block_number: T::BlockNumber }, } @@ -104,6 +103,8 @@ pub mod pallet { InvalidParam, /// Overflow adding a value to the entity property EntityPropertyOverflow, + /// No EntityProperty found for account. + EntityPropertyUnknown, /// Overflow adding a value to the entity count EntityCountOverflow, } @@ -112,7 +113,7 @@ pub mod pallet { impl Pallet { /// Create a Sense Entity for the account. - /// + /// /// Parameters: /// - `account_id`: account id. /// - `cid`: IPFS content identifier. @@ -120,6 +121,7 @@ pub mod pallet { /// Emits `EntityCreated` event when successful. /// /// Weight: `O(1)` + #[pallet::call_index(0)] #[pallet::weight(::WeightInfo::create_entity())] pub fn create_entity( origin: OriginFor, @@ -142,23 +144,14 @@ pub mod pallet { Self::save_entity(account_id.clone(), entity, count, experience, reputation, trust); Self::deposit_event(Event::EntityCreated{ - account_id, + account_id, block_number: current_block }); Ok(()) } - // TODO: - // mutation of values should be restricted - // certain roles are allowed to mutate values - // xp: realm - // rep: social - // trust: id - // all: governance - // sudo ( until its removal ) - /// Modifies a property of the account. - /// + /// /// Parameters: /// - `account_id`: account id. /// - `property_type`: property type (Experience, Reputation, Trust). @@ -167,11 +160,12 @@ pub mod pallet { /// Emits `PropertyUpdated` event when successful. /// /// Weight: `O(1)` + #[pallet::call_index(1)] #[pallet::weight(::WeightInfo::update_property())] pub fn update_property( - origin: OriginFor, - account_id: T::AccountId, - property_type: PropertyType, + origin: OriginFor, + account_id: T::AccountId, + property_type: PropertyType, value: u8 ) -> DispatchResult { ensure_root(origin)?; @@ -179,7 +173,7 @@ pub mod pallet { let current_block = >::block_number(); let v = u64::from(value); - let current = Self::get_property(property_type.clone(), account_id.clone()).unwrap(); + let current = Self::get_property(property_type.clone(), account_id.clone()).ok_or(Error::::EntityPropertyUnknown)?; let updated = EntityProperty::new( current.get_value().checked_add(v).ok_or(Error::::EntityPropertyOverflow)?, current_block @@ -189,7 +183,7 @@ pub mod pallet { Self::deposit_event(Event::PropertyUpdated{ property_type, - account_id, + account_id, block_number: current_block }); Ok(()) @@ -198,11 +192,11 @@ pub mod pallet { impl Pallet { fn save_entity( - account_id: T::AccountId, - entity: Entity>, - count: u128, - experience: EntityProperty, - reputation: EntityProperty, + account_id: T::AccountId, + entity: Entity>, + count: u128, + experience: EntityProperty, + reputation: EntityProperty, trust: EntityProperty ) { Entities::::insert(account_id.clone(), entity); diff --git a/sense/src/mock.rs b/sense/src/mock.rs index 06e65f05c..ce5881806 100644 --- a/sense/src/mock.rs +++ b/sense/src/mock.rs @@ -30,8 +30,6 @@ construct_runtime!( parameter_types! { pub const BlockHashCount: u64 = 250; pub const SS58Prefix: u8 = 42; - pub BlockWeights: frame_system::limits::BlockWeights = - frame_system::limits::BlockWeights::simple_max(1024); pub static ExistentialDeposit: u64 = 0; } @@ -40,8 +38,8 @@ impl system::Config for Test { type BlockWeights = (); type BlockLength = (); type DbWeight = (); - type Origin = Origin; - type Call = Call; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; type Index = u64; type BlockNumber = u64; type Hash = H256; @@ -49,7 +47,7 @@ impl system::Config for Test { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type Event = Event; + type RuntimeEvent = RuntimeEvent; type BlockHashCount = BlockHashCount; type Version = (); type PalletInfo = PalletInfo; @@ -67,7 +65,7 @@ parameter_types! { } impl pallet_sense::Config for Test { - type Event = Event; + type RuntimeEvent = RuntimeEvent; type WeightInfo = (); type StringLimit = StringLimit; } diff --git a/sense/src/tests.rs b/sense/src/tests.rs index a9ba04650..7ff6a026c 100644 --- a/sense/src/tests.rs +++ b/sense/src/tests.rs @@ -1,6 +1,6 @@ #![cfg(test)] use super::{Event as SenseEvent, Entity, EntityProperty, PropertyType, Error, Config, Entities, Properties}; -use crate::mock::*; +use crate::mock::{RuntimeEvent as Event, Sense, System, Test, RuntimeOrigin as Origin, new_test_ext}; use frame_support::{assert_noop, assert_ok, BoundedVec}; use frame_system::RawOrigin; use sp_runtime::traits::BadOrigin; @@ -61,6 +61,11 @@ fn should_update_properties() { ); Entities::::insert(account, Entity::new(account, block_number, 0, cid)); + + assert_noop!(Sense::update_property(RawOrigin::Root.into(), account, PropertyType::Experience, 125), Error::::EntityPropertyUnknown); + assert_noop!(Sense::update_property(RawOrigin::Root.into(), account, PropertyType::Reputation, 234), Error::::EntityPropertyUnknown); + assert_noop!(Sense::update_property(RawOrigin::Root.into(), account, PropertyType::Trust, 250), Error::::EntityPropertyUnknown); + Properties::::insert(PropertyType::Experience, account, EntityProperty::new(0, block_number)); Properties::::insert(PropertyType::Reputation, account, EntityProperty::new(0, block_number)); Properties::::insert(PropertyType::Trust, account, EntityProperty::new(0, block_number)); diff --git a/sense/src/weights.rs b/sense/src/weights.rs index 6483032ac..fff14b72c 100644 --- a/sense/src/weights.rs +++ b/sense/src/weights.rs @@ -1,35 +1,21 @@ -// This file is part of Substrate. - -// Copyright (C) 2021 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. //! Autogenerated weights for gamedao_sense //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-08-03, STEPS: `20`, REPEAT: 10, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 +//! DATE: 2023-04-19, STEPS: `20`, REPEAT: `10`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 // Executed Command: // ./target/release/subzero // benchmark // pallet +// --execution=wasm // --pallet=gamedao_sense // --extrinsic=* // --steps=20 // --repeat=10 -// --output=gamedao-protocol/sense/src/weights.rs +// --output=modules/gamedao-protocol/sense/src/weights.rs // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -48,38 +34,64 @@ pub trait WeightInfo { /// Weights for gamedao_sense using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Sense Entities (r:1 w:1) - // Storage: Sense EntityCount (r:1 w:1) - // Storage: Sense Properties (r:0 w:3) + /// Storage: Sense Entities (r:1 w:1) + /// Proof: Sense Entities (max_values: None, max_size: Some(170), added: 2645, mode: MaxEncodedLen) + /// Storage: Sense EntityCount (r:1 w:1) + /// Proof: Sense EntityCount (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: Sense Properties (r:0 w:3) + /// Proof: Sense Properties (max_values: None, max_size: Some(77), added: 2552, mode: MaxEncodedLen) fn create_entity() -> Weight { - (22_100_000 as Weight) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(5 as Weight)) + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `5136` + // Minimum execution time: 24_000 nanoseconds. + Weight::from_parts(25_000_000, 5136) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) } - // Storage: Sense Entities (r:1 w:0) - // Storage: Sense Properties (r:1 w:1) + /// Storage: Sense Entities (r:1 w:0) + /// Proof: Sense Entities (max_values: None, max_size: Some(170), added: 2645, mode: MaxEncodedLen) + /// Storage: Sense Properties (r:1 w:1) + /// Proof: Sense Properties (max_values: None, max_size: Some(77), added: 2552, mode: MaxEncodedLen) fn update_property() -> Weight { - (19_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(2 as Weight)) - .saturating_add(T::DbWeight::get().writes(1 as Weight)) + // Proof Size summary in bytes: + // Measured: `452` + // Estimated: `7177` + // Minimum execution time: 21_000 nanoseconds. + Weight::from_parts(22_000_000, 7177) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Sense Entities (r:1 w:1) - // Storage: Sense EntityCount (r:1 w:1) - // Storage: Sense Properties (r:0 w:3) + /// Storage: Sense Entities (r:1 w:1) + /// Proof: Sense Entities (max_values: None, max_size: Some(170), added: 2645, mode: MaxEncodedLen) + /// Storage: Sense EntityCount (r:1 w:1) + /// Proof: Sense EntityCount (max_values: Some(1), max_size: Some(16), added: 511, mode: MaxEncodedLen) + /// Storage: Sense Properties (r:0 w:3) + /// Proof: Sense Properties (max_values: None, max_size: Some(77), added: 2552, mode: MaxEncodedLen) fn create_entity() -> Weight { - (22_100_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(5 as Weight)) + // Proof Size summary in bytes: + // Measured: `142` + // Estimated: `5136` + // Minimum execution time: 24_000 nanoseconds. + Weight::from_parts(25_000_000, 5136) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) } - // Storage: Sense Entities (r:1 w:0) - // Storage: Sense Properties (r:1 w:1) + /// Storage: Sense Entities (r:1 w:0) + /// Proof: Sense Entities (max_values: None, max_size: Some(170), added: 2645, mode: MaxEncodedLen) + /// Storage: Sense Properties (r:1 w:1) + /// Proof: Sense Properties (max_values: None, max_size: Some(77), added: 2552, mode: MaxEncodedLen) fn update_property() -> Weight { - (19_000_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(2 as Weight)) - .saturating_add(RocksDbWeight::get().writes(1 as Weight)) + // Proof Size summary in bytes: + // Measured: `452` + // Estimated: `7177` + // Minimum execution time: 21_000 nanoseconds. + Weight::from_parts(22_000_000, 7177) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } -} +} \ No newline at end of file diff --git a/signal/Cargo.toml b/signal/Cargo.toml index dd4fa23f9..c1955ce62 100644 --- a/signal/Cargo.toml +++ b/signal/Cargo.toml @@ -1,12 +1,6 @@ -# ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -# ███░▄▄▄█░▄▄▀█░▄▀▄░█░▄▄█░▄▀█░▄▄▀█▀▄▄▀██ -# ███░█▄▀█░▀▀░█░█▄█░█░▄▄█░█░█░▀▀░█░██░██ -# ███▄▄▄▄█▄██▄█▄███▄█▄▄▄█▄▄██▄██▄██▄▄███ -# ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ - [package] name = "gamedao-signal" -version = "1.2.0" +version = "1.3.0" authors = ["zero.io","gamedao.co"] repository = "https://github.com/gamedaoco/gamedao-protocol" edition = "2018" @@ -21,35 +15,39 @@ categories = [ ] [dependencies] -serde = { version = "1.0.143", optional = true } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +serde = { version = "1.0.143", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28", default-features = false } -sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28", default-features = false } -frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28", default-features = false } -frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28", default-features = false } -frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28", default-features = false, optional = true } -orml-traits = { path = "../../orml/traits", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false } +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false, optional = true } +orml-traits = { git = 'https://github.com/open-web3-stack/open-runtime-module-library', branch= "polkadot-v0.9.43", default-features = false } gamedao-traits = { package = "gamedao-traits", path = "../traits", default-features = false } -sp-io = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.28", default-features=false } +sp-io = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.43", default-features=false } [dev-dependencies] -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28" } -sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28" } +sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.43" } +sp-core = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.43" } -frame-support-test = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.28" } -pallet-balances = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28" } -pallet-timestamp = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28" } +pallet-balances = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } +pallet-timestamp = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43" } -orml-tokens = { path = "../../orml/tokens", default-features = false } -orml-currencies = { path = "../../orml/currencies", default-features = false } +orml-currencies = { git = "https://github.com/open-web3-stack/open-runtime-module-library", branch= "polkadot-v0.9.43", default-features = false } +orml-tokens = { git = "https://github.com/open-web3-stack/open-runtime-module-library", branch= "polkadot-v0.9.43", default-features = false } gamedao-control = { path = "../control", default-features = true } gamedao-flow = { path = "../flow", default-features = true } [features] default = ["std"] -runtime-benchmarks = ["frame-benchmarking"] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "gamedao-traits/runtime-benchmarks", + "gamedao-control/runtime-benchmarks", + "gamedao-flow/runtime-benchmarks" +] std = [ "codec/std", "serde/std", @@ -59,9 +57,7 @@ std = [ "frame-system/std", "frame-benchmarking/std", - "sp-core/std", "sp-std/std", - "sp-runtime/std", "orml-traits/std", "orml-tokens/std", diff --git a/signal/src/benchmarking.rs b/signal/src/benchmarking.rs index 507484b05..b4fcb7478 100644 --- a/signal/src/benchmarking.rs +++ b/signal/src/benchmarking.rs @@ -1,14 +1,14 @@ #![cfg(feature = "runtime-benchmarks")] +use super::*; +use crate::Pallet as Signal; + use frame_benchmarking::{account, benchmarks, impl_benchmark_test_suite, whitelisted_caller}; use frame_system::RawOrigin; use frame_support::{dispatch::DispatchError, traits::{Get, Hooks}, BoundedVec}; use sp_runtime::traits::SaturatedConversion; use sp_std::vec::Vec; -use crate::*; - - const SEED: u32 = 0; const DEPOSIT_AMOUNT: u128 = 10_000_000_000_000_000_000; @@ -28,26 +28,27 @@ fn fund_accounts(account_ids: &Vec) -> Result<(), Dispa } fn create_org_campaign(caller: T::AccountId, contributors_count: u32, members: Option>) -> Result<(T::Hash, T::Hash), DispatchError> { - let org_id = T::Control::create_org(caller.clone())?; + let org_id = T::ControlBenchmarkHelper::create_org(caller.clone())?; let treasury_account_id = T::Control::org_treasury_account(&org_id).unwrap(); fund_account::(&treasury_account_id)?; let now = frame_system::Pallet::::block_number(); - let campaign_id = T::Flow::create_campaign(&caller, &org_id, now)?; + let campaign_id = T::FlowBenchmarkHelper::create_campaign(&caller, &org_id, now)?; let contributors: Vec = (0..contributors_count).collect::>().iter() .map(|i| account("contributor", *i, SEED)).collect(); fund_accounts::(&contributors)?; - T::Flow::create_contributions(&campaign_id, contributors)?; + T::FlowBenchmarkHelper::create_contributions(&campaign_id, contributors)?; let current_block = frame_system::Pallet::::block_number(); - let mut expiry = current_block + 200_u32.into(); + let mut expiry = current_block + 57_600_u32.into(); for _ in 0 .. 10 { - T::Flow::finalize_campaigns_by_block(expiry); + + T::FlowBenchmarkHelper::finalize_campaigns_by_block(expiry); if T::Flow::is_campaign_succeeded(&campaign_id) { break; } expiry = expiry + 1_u32.into(); } if let Some(members) = members { - T::Control::fill_org_with_members(&org_id, members)?; + T::ControlBenchmarkHelper::fill_org_with_members(&org_id, members)?; } Ok((campaign_id, org_id)) } @@ -60,8 +61,9 @@ benchmarks! { fund_account::(&caller)?; let (campaign_id, org_id) = create_org_campaign::(caller.clone(), 10, None)?; let bounded_str = BoundedVec::truncate_from((0..255).collect()); + frame_system::Pallet::::set_block_number(3u32.into()); let start = frame_system::Pallet::::block_number(); - let expiry = frame_system::Pallet::::block_number() + 200_u32.into(); + let expiry = frame_system::Pallet::::block_number() + T::ProposalDurationLimits::get().0; let deposit = T::MinProposalDeposit::get(); let amount: T::Balance = 10_000u32.saturated_into(); let currency = T::PaymentTokenId::get(); @@ -102,8 +104,9 @@ benchmarks! { } let (campaign_id, org_id) = create_org_campaign::(proposer.clone(), 10, Some(members.clone()))?; let bounded_str: BoundedVec = BoundedVec::truncate_from((0..255).collect()); + frame_system::Pallet::::set_block_number(3u32.into()); let start = frame_system::Pallet::::block_number(); - let expiry = frame_system::Pallet::::block_number() + 200_u32.into(); + let expiry = frame_system::Pallet::::block_number() + T::ProposalDurationLimits::get().0; let deposit = T::MinProposalDeposit::get(); let amount: T::Balance = 10_000u32.saturated_into(); let currency_id = T::PaymentTokenId::get(); @@ -163,8 +166,9 @@ benchmarks! { fund_account::(&caller)?; let (campaign_id, org_id) = create_org_campaign::(caller.clone(), 10, None)?; let bounded_str: BoundedVec = BoundedVec::truncate_from((0..255).collect()); - let start = frame_system::Pallet::::block_number() + 10_u32.into(); - let expiry = frame_system::Pallet::::block_number() + 200_u32.into(); + frame_system::Pallet::::set_block_number(3u32.into()); + let start = frame_system::Pallet::::block_number(); + let expiry = frame_system::Pallet::::block_number() + T::ProposalDurationLimits::get().0; let deposit = T::MinProposalDeposit::get(); let amount: T::Balance = 10_000u32.saturated_into(); let currency_id = T::PaymentTokenId::get(); @@ -186,7 +190,7 @@ benchmarks! { prop.campaign_id, prop.amount, prop.beneficiary, prop.currency_id, )?; // Ensure that proposal exists and Activated - assert!(ProposalStates::::get(&proposal_id) == ProposalState::Created); + assert!(ProposalStates::::get(&proposal_id) == ProposalState::Active); } }: { Pallet::::on_initialize(start); } @@ -206,6 +210,6 @@ benchmarks! { } } - impl_benchmark_test_suite!(Signal, crate::tests::new_test_ext(), crate::tests::Test); + impl_benchmark_test_suite!(Signal, crate::mock::ExtBuilder::default().build(), crate::mock::Test); } diff --git a/signal/src/lib.rs b/signal/src/lib.rs index ac0e7a12f..79c904770 100644 --- a/signal/src/lib.rs +++ b/signal/src/lib.rs @@ -14,7 +14,6 @@ #![cfg_attr(not(feature = "std"), no_std)] #![allow(deprecated)] // TODO: clean transactional pub mod types; -pub mod migration; #[cfg(test)] pub mod mock; @@ -26,9 +25,10 @@ pub mod weights; use frame_support::{ BoundedVec, - traits::{StorageVersion, BalanceStatus}, + traits::BalanceStatus, dispatch::DispatchResult, weights::Weight, + log, transactional }; use frame_system::ensure_signed; @@ -39,7 +39,9 @@ use sp_runtime::{ }; use sp_std::vec; -use gamedao_traits::{ControlTrait, ControlBenchmarkingTrait, FlowTrait, FlowBenchmarkingTrait}; +#[cfg(feature = "runtime-benchmarks")] +use gamedao_traits::{ControlBenchmarkingTrait, FlowBenchmarkingTrait}; +use gamedao_traits::{ControlTrait, FlowTrait}; use types::{ ProposalIndex, ProposalType, ProposalState, SlashingRule, @@ -50,8 +52,8 @@ pub use pallet::*; pub use weights::WeightInfo; type Proposal = types::Proposal< - ::Hash, ::BlockNumber, - ::AccountId, ::Balance, + ::Hash, ::BlockNumber, + ::AccountId, ::Balance, ::CurrencyId, BoundedVec::StringLimit> >; @@ -67,19 +69,14 @@ pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; - /// The current storage version. - const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); - #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] - #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); #[pallet::config] pub trait Config: frame_system::Config { - type Event: From> - + IsType<::Event> - + Into<::Event>; + type RuntimeEvent: From> + + IsType<::RuntimeEvent> + + Into<::RuntimeEvent>; /// The units in which we record balances. type Balance: Member @@ -107,16 +104,20 @@ pub mod pallet { + MultiReservableCurrency; /// Control pallet's public interface. - type Control: ControlTrait - + ControlBenchmarkingTrait; + type Control: ControlTrait; /// Flow pallet's public interface. - type Flow: FlowTrait - + FlowBenchmarkingTrait; + type Flow: FlowTrait; + + #[cfg(feature = "runtime-benchmarks")] + type ControlBenchmarkHelper: ControlBenchmarkingTrait; + + #[cfg(feature = "runtime-benchmarks")] + type FlowBenchmarkHelper: FlowBenchmarkingTrait; /// Weight information for extrinsics in this module. type WeightInfo: WeightInfo; - + /// The CurrencyId which is used as a payment token. #[pallet::constant] type PaymentTokenId: Get; @@ -163,7 +164,7 @@ pub mod pallet { /// /// Proposals: map Hash => Proposal #[pallet::storage] - pub(super) type ProposalOf = + pub(super) type ProposalOf = StorageMap<_, Blake2_128Concat, T::Hash, Proposal, OptionQuery>; @@ -171,7 +172,7 @@ pub mod pallet { /// /// ProposalStates: map Hash => ProposalState #[pallet::storage] - pub(super) type ProposalStates = + pub(super) type ProposalStates = StorageMap<_, Blake2_128Concat, T::Hash, ProposalState, ValueQuery, GetDefault>; /// Proposals ending in a block. @@ -180,7 +181,7 @@ pub mod pallet { #[pallet::storage] pub(super) type ProposalsByBlock = StorageDoubleMap<_, Blake2_128Concat, BlockType, Blake2_128Concat, T::BlockNumber, BoundedVec, ValueQuery>; - + #[pallet::storage] pub type ProposalCount = StorageValue<_, ProposalIndex, ValueQuery>; @@ -233,12 +234,14 @@ pub mod pallet { OrgInactive, OutOfBounds, ProposalExists, + ProposalInvalid, ProposalNotActive, ProposalUnknown, TooManyProposals, TreasuryBalanceLow, TreasuryUnknown, VoteLimitReached, + VotingInvalid, WrongParameter, } @@ -293,7 +296,7 @@ pub mod pallet { ensure!(scale != Scale::Quadratic, Error::::WrongParameter); } Unit::Token => { - // Since it's not possible to calculate eligible voting power, + // Since it's not possible to calculate eligible voting power, // Absolute majority and quorum doesn't work for Unit::Token ensure!(majority != Majority::Absolute, Error::::WrongParameter); ensure!(quorum.is_none(), Error::::WrongParameter); @@ -311,8 +314,8 @@ pub mod pallet { let campaign_owner = T::Flow::campaign_owner(&c_id).ok_or(Error::::AuthorizationError)?; ensure!(proposer == campaign_owner, Error::::AuthorizationError); ensure!(T::Flow::is_campaign_succeeded(&c_id), Error::::CampaignUnsucceeded); - - let used_balance = CampaignBalanceUsed::::get(&c_id); + + let used_balance = CampaignBalanceUsed::::get(c_id); let total_balance = T::Flow::campaign_balance(&c_id); let remaining_balance = total_balance .checked_sub(&used_balance) @@ -330,12 +333,12 @@ pub mod pallet { // Create Proposal let index = ProposalCount::::get(); let proposal = types::Proposal { - index: index.clone(), title, cid, org_id, campaign_id, amount, deposit: proposal_deposit, + index, title, cid, org_id, campaign_id, amount, deposit: proposal_deposit, currency_id, beneficiary, proposal_type: proposal_type.clone(), start: starts, expiry, owner: proposer.clone(), slashing_rule: SlashingRule::Automated }; let proposal_hash = T::Hashing::hash_of(&proposal); - ensure!(!ProposalOf::::contains_key(&proposal_hash), Error::::ProposalExists); + ensure!(!ProposalOf::::contains_key(proposal_hash), Error::::ProposalExists); Self::create_proposal(&proposal_hash, proposal)?; Self::create_voting(&proposal_hash, &proposal_type, &index, &org_id, &campaign_id, quorum, majority, unit, scale); @@ -361,7 +364,7 @@ pub mod pallet { deposit: Option, ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; - let voting = ProposalVoting::::get(&proposal_id).ok_or(Error::::ProposalUnknown)?; + let voting = ProposalVoting::::get(proposal_id).ok_or(Error::::ProposalUnknown)?; // Deposit is required for token weighted voting only if voting.unit == Unit::Token && deposit.is_none() { @@ -370,7 +373,7 @@ pub mod pallet { return Err(Error::::WrongParameter)?; } - let proposal = ProposalOf::::get(&proposal_id).ok_or(Error::::ProposalUnknown)?; + let proposal = ProposalOf::::get(proposal_id).ok_or(Error::::ProposalUnknown)?; match proposal.proposal_type { ProposalType::General | ProposalType::Spending => { ensure!( @@ -380,6 +383,7 @@ pub mod pallet { ); }, ProposalType::Withdrawal => { + ensure!(proposal.campaign_id.is_some(), Error::::ProposalInvalid); ensure!( T::Flow::is_campaign_contributor(&proposal.campaign_id.unwrap(), &who), Error::::AuthorizationError @@ -389,7 +393,7 @@ pub mod pallet { // Ensure the Proposal is Active ensure!( - ProposalStates::::get(&proposal_id) == ProposalState::Active, + ProposalStates::::get(proposal_id) == ProposalState::Active, Error::::ProposalNotActive ); @@ -401,75 +405,75 @@ pub mod pallet { #[pallet::hooks] impl Hooks> for Pallet { - fn on_runtime_upgrade() -> Weight { - migration::migrate::() - } fn on_initialize(block_number: T::BlockNumber) -> Weight { - let proposals = ProposalsByBlock::::get(BlockType::Start, &block_number); + let proposals = ProposalsByBlock::::get(BlockType::Start, block_number); for proposal_id in &proposals { - let proposal_state = ProposalStates::::get(&proposal_id); + let proposal_state = ProposalStates::::get(proposal_id); if proposal_state != ProposalState::Created { continue; // Just a safety check, never should happen }; ProposalStates::::insert(proposal_id, ProposalState::Active); Self::deposit_event(Event::::Activated { proposal_id: *proposal_id }); } + T::WeightInfo::on_initialize(proposals.len().saturated_into()) } fn on_finalize(block_number: T::BlockNumber) { - for proposal_id in &ProposalsByBlock::::get(BlockType::Expiry, &block_number) { + for proposal_id in &ProposalsByBlock::::get(BlockType::Expiry, block_number) { // Skip already finalized proposals (ex. if absolute majority was achieved) - let mut proposal_state = ProposalStates::::get(&proposal_id); + let mut proposal_state = ProposalStates::::get(proposal_id); if proposal_state != ProposalState::Active { continue; }; - let voting_exists = ProposalVoting::::contains_key(&proposal_id); - let proposal_exists = ProposalOf::::contains_key(&proposal_id); + let maybe_voting = ProposalVoting::::get(proposal_id); + let proposal_exists = ProposalOf::::contains_key(proposal_id); - if !voting_exists || !proposal_exists { + if maybe_voting.is_none() || !proposal_exists { + log::error!(target: "runtime::gamedao_signal", "Proposal [{:?}] or voting [{:?}] is missing for proposal id: {:?}.", proposal_exists, maybe_voting.is_some(), proposal_id); continue; // should never happen } - let voting = ProposalVoting::::get(&proposal_id).unwrap(); + let voting = maybe_voting.unwrap(); // Get the final state based on Voting participation, quorum, majority proposal_state = Self::get_final_proposal_state(&voting); - - Self::finalize_proposal(&proposal_id, proposal_state, &voting); + + if Self::finalize_proposal(proposal_id, proposal_state, &voting).is_err() { + log::error!(target: "runtime::gamedao_signal", "Failed to finalize a proposal {:?}.", proposal_id); + }; } } } impl Pallet { - pub fn get_voting_power(voting: &Voting, deposit: &Option) -> VotingPower { + pub fn get_voting_power(voting: &Voting, deposit: &Option) -> Result { let mut power: VotingPower = 1; match voting.unit { Unit::Account => { match voting.scale { - Scale::Linear => { + Scale::Linear => { power = 1; } - Scale::Quadratic => { + Scale::Quadratic => { // So far not possible, maybe in case of delegation } } } Unit::Token => { - let linear_power: VotingPower = deposit.unwrap().saturated_into(); + let linear_power: VotingPower = deposit.ok_or(Error::::MissingParameter)?.saturated_into(); match voting.scale { - Scale::Linear => { + Scale::Linear => { power = linear_power; } - Scale::Quadratic => { - let linear_power: VotingPower = deposit.unwrap().saturated_into(); + Scale::Quadratic => { power = linear_power.integer_sqrt(); } } } } - power + Ok(power) } pub fn process_voting_deposits( @@ -481,12 +485,12 @@ pub mod pallet { return Ok(()); }; if let Some(amount) = old_deposit { - let _ = T::Currency::unreserve(T::ProtocolTokenId::get(), &who, *amount); + let _ = T::Currency::unreserve(T::ProtocolTokenId::get(), who, *amount); } if let Some(amount) = deposit { - T::Currency::reserve(T::ProtocolTokenId::get(), &who, *amount).map_err(|_| Error::::BalanceLow)?; + T::Currency::reserve(T::ProtocolTokenId::get(), who, *amount).map_err(|_| Error::::BalanceLow)?; } - return Ok(()); + Ok(()) } pub fn try_finalize_proposal(voting: &Voting) -> Option { @@ -504,9 +508,9 @@ pub mod pallet { } // Everyone voted if voting.eligible == voting.participating { - return Some(Self::get_final_proposal_state(&voting)); + return Some(Self::get_final_proposal_state(voting)); } - return None; + None } pub fn do_vote( @@ -518,7 +522,7 @@ pub mod pallet { ) -> Result { let position_yes = voting.ayes.iter().position(|a| a.0 == who); let position_no = voting.nays.iter().position(|a| a.0 == who); - let power = Self::get_voting_power(&voting, &deposit); + let power = Self::get_voting_power(&voting, &deposit)?; let mut old_deposit: Option = None; if approve { @@ -527,7 +531,7 @@ pub mod pallet { voting.nays.swap_remove(pos); } if position_yes.is_none() { - voting.ayes.try_push((who.clone(), power.clone(), deposit.clone())).map_err(|_| Error::::VoteLimitReached)?; + voting.ayes.try_push((who.clone(), power, deposit)).map_err(|_| Error::::VoteLimitReached)?; } else { return Err(Error::::DuplicateVote.into()) } @@ -537,7 +541,7 @@ pub mod pallet { voting.ayes.swap_remove(pos); } if position_no.is_none() { - voting.nays.try_push((who.clone(), power.clone(), deposit.clone())).map_err(|_| Error::::VoteLimitReached)?; + voting.nays.try_push((who.clone(), power, deposit)).map_err(|_| Error::::VoteLimitReached)?; } else { return Err(Error::::DuplicateVote.into()) } @@ -558,11 +562,11 @@ pub mod pallet { no: voting.no, }); - ProposalVoting::::insert(&proposal_id, &voting); + ProposalVoting::::insert(proposal_id, &voting); // For Absolute majority if more then 50% of members vote for one option, the proposal period ends earlier. if let Some(final_proposal_state) = Self::try_finalize_proposal(&voting) { - Self::finalize_proposal(&proposal_id, final_proposal_state, &voting); + Self::finalize_proposal(&proposal_id, final_proposal_state, &voting)?; } Ok(voting.participating as u32) @@ -572,12 +576,12 @@ pub mod pallet { proposal_id: &T::Hash, proposal: Proposal ) -> Result<(), DispatchError> { - let proposal_state; - if proposal.start > >::block_number() { - proposal_state = ProposalState::Created; - } else { - proposal_state = ProposalState::Active; - } + let proposal_state = + if proposal.start > >::block_number() { + ProposalState::Created + } else { + ProposalState::Active + }; T::Currency::reserve( T::ProtocolTokenId::get(), &proposal.owner, proposal.deposit ).map_err(|_| Error::::BalanceLow)?; @@ -591,13 +595,13 @@ pub mod pallet { ProposalsByBlock::::try_mutate( BlockType::Start, proposal.start, |proposals| -> Result<(), DispatchError> { - proposals.try_push(proposal_id.clone()).map_err(|_| Error::::TooManyProposals)?; + proposals.try_push(*proposal_id).map_err(|_| Error::::TooManyProposals)?; Ok(()) } )?; ProposalsByBlock::::try_mutate( BlockType::Expiry, proposal.expiry, |proposals| -> Result<(), DispatchError> { - proposals.try_push(proposal_id.clone()).map_err(|_| Error::::TooManyProposals)?; + proposals.try_push(*proposal_id).map_err(|_| Error::::TooManyProposals)?; Ok(()) } )?; @@ -629,7 +633,7 @@ pub mod pallet { eligible = T::Flow::campaign_contributors_count(&campaign_id.unwrap()).into(); } _ => { - eligible = T::Control::org_member_count(&org_id).into(); + eligible = T::Control::org_member_count(org_id).into(); } } } @@ -665,50 +669,53 @@ pub mod pallet { // Simple majority should be implemented for multiple options voting Majority::Relative | Majority::Simple => { if voting.yes > voting.no { - return ProposalState::Accepted; + ProposalState::Accepted } else { - return ProposalState::Rejected; + ProposalState::Rejected } } Majority::Absolute => { let majority_quorum = Permill::from_rational(1u32, 2u32); if voting.yes >= majority_quorum.mul_floor(voting.eligible) { - return ProposalState::Accepted; + ProposalState::Accepted } else { - return ProposalState::Rejected; + ProposalState::Rejected } } } } - fn apply_proposal_actions(proposal: &Proposal, proposal_state: ProposalState) -> ProposalState { + fn apply_proposal_actions(proposal: &Proposal, proposal_state: ProposalState) -> Result { match proposal.proposal_type { ProposalType::Withdrawal => { - let campaign_id = proposal.campaign_id.unwrap(); - let amount = proposal.amount.unwrap(); - T::Currency::unreserve( - proposal.currency_id.unwrap(), - &T::Control::org_treasury_account(&proposal.org_id).unwrap(), amount); - let used_balance = CampaignBalanceUsed::::get(&campaign_id); - CampaignBalanceUsed::::insert(&campaign_id, used_balance + amount); - return ProposalState::Finalized; + let campaign_id = proposal.campaign_id.ok_or(Error::::ProposalInvalid)?; + let amount = proposal.amount.ok_or(Error::::ProposalInvalid)?; + let currency_id = proposal.currency_id.ok_or(Error::::ProposalInvalid)?; + let treasury = T::Control::org_treasury_account(&proposal.org_id).ok_or(Error::::TreasuryUnknown)?; + T::Currency::unreserve(currency_id, &treasury, amount); + let used_balance = CampaignBalanceUsed::::get(campaign_id); + CampaignBalanceUsed::::insert(campaign_id, used_balance + amount); + Ok(ProposalState::Finalized) } ProposalType::Spending => { - let res = T::Currency::repatriate_reserved( - proposal.currency_id.unwrap(), - &T::Control::org_treasury_account(&proposal.org_id).unwrap(), - &proposal.beneficiary.as_ref().unwrap(), - proposal.amount.unwrap(), - BalanceStatus::Free); - debug_assert!(res.is_ok()); - return ProposalState::Finalized; + let amount = proposal.amount.ok_or(Error::::ProposalInvalid)?; + let currency_id = proposal.currency_id.ok_or(Error::::ProposalInvalid)?; + let treasury = T::Control::org_treasury_account(&proposal.org_id).ok_or(Error::::TreasuryUnknown)?; + let beneficiary = proposal.beneficiary.as_ref().ok_or(Error::::ProposalInvalid)?; + T::Currency::repatriate_reserved( + currency_id, + &treasury, + beneficiary, + amount, + BalanceStatus::Free)?; + Ok(ProposalState::Finalized) } - _ => { return proposal_state } + _ => { Ok(proposal_state) } } } - fn process_proposal_deposit(proposal: &Proposal, voting: &Voting, proposal_state: &ProposalState) { + fn process_proposal_deposit(proposal: &Proposal, voting: &Voting, proposal_state: &ProposalState) -> DispatchResult { let currency_id = T::ProtocolTokenId::get(); match proposal_state { ProposalState::Rejected => { @@ -721,12 +728,9 @@ pub mod pallet { let gamedao_share = T::GameDAOGetsFromSlashing::get().mul_floor(proposal.deposit); let org_share = proposal.deposit - gamedao_share; let gamedo_trsry = T::GameDAOTreasury::get(); - let org_trsry = T::Control::org_treasury_account(&proposal.org_id).unwrap(); - - let res = T::Currency::transfer(currency_id, &proposal.owner, &gamedo_trsry, gamedao_share); - debug_assert!(res.is_ok()); - let res = T::Currency::transfer(currency_id, &proposal.owner, &org_trsry, org_share); - debug_assert!(res.is_ok()); + let org_trsry = T::Control::org_treasury_account(&proposal.org_id).ok_or(Error::::TreasuryUnknown)?; + T::Currency::transfer(currency_id, &proposal.owner, &gamedo_trsry, gamedao_share)?; + T::Currency::transfer(currency_id, &proposal.owner, &org_trsry, org_share)?; } } SlashingRule::Tribunal => { @@ -736,40 +740,43 @@ pub mod pallet { } _ => { T::Currency::unreserve(currency_id, &proposal.owner, proposal.deposit); } } - + Ok(()) } fn emit_event(proposal_state: &ProposalState, proposal_id: &T::Hash) { match proposal_state { ProposalState::Accepted => { - Self::deposit_event(Event::::Accepted { proposal_id: proposal_id.clone() }); + Self::deposit_event(Event::::Accepted { proposal_id: *proposal_id }); } ProposalState::Rejected => { - Self::deposit_event(Event::::Rejected { proposal_id: proposal_id.clone() }); + Self::deposit_event(Event::::Rejected { proposal_id: *proposal_id }); } ProposalState::Expired => { - Self::deposit_event(Event::::Expired { proposal_id: proposal_id.clone() }); + Self::deposit_event(Event::::Expired { proposal_id: *proposal_id }); } ProposalState::Finalized => { - Self::deposit_event(Event::::Finalized { proposal_id: proposal_id.clone() }); + Self::deposit_event(Event::::Finalized { proposal_id: *proposal_id }); } _ => { } } } - fn finalize_proposal(proposal_id: &T::Hash, mut proposal_state: ProposalState, voting: &Voting) { - let proposal = ProposalOf::::get(&proposal_id).unwrap(); // should not fail, checked before + fn finalize_proposal(proposal_id: &T::Hash, mut proposal_state: ProposalState, voting: &Voting) -> DispatchResult { + let proposal = ProposalOf::::get(proposal_id).ok_or(Error::::ProposalUnknown)?; match proposal_state { ProposalState::Accepted => { - proposal_state = Self::apply_proposal_actions(&proposal, proposal_state); + proposal_state = Self::apply_proposal_actions(&proposal, proposal_state)?; } _ => { if proposal.proposal_type == ProposalType::Spending { + let amount = proposal.amount.ok_or(Error::::ProposalInvalid)?; + let currency_id = proposal.currency_id.ok_or(Error::::ProposalInvalid)?; + let treasury = T::Control::org_treasury_account(&proposal.org_id).ok_or(Error::::TreasuryUnknown)?; T::Currency::unreserve( - proposal.currency_id.unwrap(), - & T::Control::org_treasury_account(&proposal.org_id).unwrap(), - proposal.amount.unwrap()); + currency_id, + &treasury, + amount); }; } } @@ -778,17 +785,19 @@ pub mod pallet { let currency_id = T::ProtocolTokenId::get(); // TODO: chain - &voting.ayes.iter().chain(&voting.nays.iter()) for (who, _, deposit) in &voting.ayes { - let _ = T::Currency::unreserve(currency_id, &who, deposit.unwrap()); + let _ = T::Currency::unreserve(currency_id, who, deposit.ok_or(Error::::VotingInvalid)?); }; for (who, _, deposit) in &voting.nays { - let _ = T::Currency::unreserve(currency_id, &who, deposit.unwrap()); + let _ = T::Currency::unreserve(currency_id, who, deposit.ok_or(Error::::VotingInvalid)?); }; } // Refund or slash proposal's deposit based on proposal state and majority of rejection - Self::process_proposal_deposit(&proposal, &voting, &proposal_state); + Self::process_proposal_deposit(&proposal, voting, &proposal_state)?; - Self::emit_event(&proposal_state, &proposal_id); + Self::emit_event(&proposal_state, proposal_id); ProposalStates::::insert(proposal_id, proposal_state); + + Ok(()) } } } diff --git a/signal/src/migration.rs b/signal/src/migration.rs deleted file mode 100644 index b3888b558..000000000 --- a/signal/src/migration.rs +++ /dev/null @@ -1,47 +0,0 @@ -// _______ ________ ________ ________ ______ _______ _______ -// ╱╱ ╲╱ ╲╱ ╲╱ ╲_╱ ╲╲╱ ╲╲╱ ╲╲ -// ╱╱ __╱ ╱ ╱ ╱ ╱╱ ╱╱ ╱╱ -// ╱ ╱ ╱ ╱ ╱ _╱ ╱ ╱ ╱ -// ╲________╱╲___╱____╱╲__╱__╱__╱╲________╱╲________╱╲___╱____╱╲________╱ -// -// This file is part of GameDAO Protocol. -// Copyright (C) 2018-2022 GameDAO AG. -// SPDX-License-Identifier: Apache-2.0 - -use crate::{Config, Pallet, Weight}; -use frame_support::{ - traits::{Get, PalletInfoAccess}, -}; - -pub fn migrate() -> Weight { - use frame_support::traits::StorageVersion; - - let version = StorageVersion::get::>(); - let mut weight: Weight = 0; - - if version < 1 { - weight = weight.saturating_add(v1::migrate::()); - StorageVersion::new(1).put::>(); - } - if version < 2 { - weight = weight.saturating_add(v1::migrate::()); - StorageVersion::new(2).put::>(); - } - - weight -} - -/// V1: Clean up pallet storage -mod v1 { - use super::*; - use sp_io::hashing::twox_128; - - pub fn migrate() -> Weight { - - let _ = frame_support::storage::unhashed::clear_prefix( - &twox_128(>::name().as_bytes()), None, None - ); - - T::DbWeight::get().writes(1) - } -} diff --git a/signal/src/mock.rs b/signal/src/mock.rs index ec3207321..64f4b4f8d 100644 --- a/signal/src/mock.rs +++ b/signal/src/mock.rs @@ -15,7 +15,6 @@ use sp_runtime::{ Permill, }; use sp_std::convert::{TryFrom, TryInto}; -use gamedao_traits::FlowTrait; pub type AccountId = u64; pub type Amount = i128; @@ -24,7 +23,7 @@ pub type BlockNumber = u64; pub type CurrencyId = u32; pub type Hash = H256; pub type Moment = u64; -// pub type BoundedString = BoundedVec::StringLimit>; +pub type BoundedString = BoundedVec::StringLimit>; pub const MILLICENTS: Balance = 1_000_000_000; pub const CENTS: Balance = 1_000 * MILLICENTS; @@ -85,25 +84,23 @@ parameter_type_with_key! { }; } impl orml_tokens::Config for Test { - type Event = Event; + type RuntimeEvent = RuntimeEvent; type Balance = Balance; type Amount = Amount; type CurrencyId = CurrencyId; type WeightInfo = (); type ExistentialDeposits = ExistentialDeposits; - type OnDust = (); - type OnNewTokenAccount = (); - type OnKilledTokenAccount = (); + type CurrencyHooks = (); type MaxLocks = (); - type DustRemovalWhitelist = Nothing; - type ReserveIdentifier = ReserveIdentifier; type MaxReserves = MaxReserves; + type ReserveIdentifier = ReserveIdentifier; + type DustRemovalWhitelist = Nothing; } impl pallet_balances::Config for Test { type Balance = Balance; type DustRemoval = (); - type Event = Event; + type RuntimeEvent = RuntimeEvent; type ExistentialDeposit = ExistentialDeposit; type AccountStore = frame_system::Pallet; type MaxLocks = (); @@ -123,8 +120,8 @@ impl orml_currencies::Config for Test { parameter_types! { pub const BlockHashCount: u64 = 250; pub const SS58Prefix: u8 = 42; - pub BlockWeights: frame_system::limits::BlockWeights = - frame_system::limits::BlockWeights::simple_max(1024); + // pub BlockWeights: frame_system::limits::BlockWeights = + // frame_system::limits::BlockWeights::simple_max(1024); pub const ExistentialDeposit: Balance = 1; } impl frame_system::Config for Test { @@ -132,8 +129,8 @@ impl frame_system::Config for Test { type BlockWeights = (); type BlockLength = (); type DbWeight = (); - type Origin = Origin; - type Call = Call; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; type Index = u64; type BlockNumber = u64; type Hash = Hash; @@ -141,7 +138,7 @@ impl frame_system::Config for Test { type AccountId = AccountId; type Lookup = IdentityLookup; type Header = Header; - type Event = Event; + type RuntimeEvent = RuntimeEvent; type BlockHashCount = BlockHashCount; type Version = (); type PalletInfo = PalletInfo; @@ -175,7 +172,7 @@ impl gamedao_control::Config for Test { type Balance = Balance; type CurrencyId = CurrencyId; type WeightInfo = (); - type Event = Event; + type RuntimeEvent = RuntimeEvent; type Currency = Currencies; type MaxMembers = MaxMembers; type ProtocolTokenId = ProtocolTokenId; @@ -197,12 +194,14 @@ parameter_types! { } impl gamedao_flow::Config for Test { - type Event = Event; + type RuntimeEvent = RuntimeEvent; type Balance = Balance; type CurrencyId = CurrencyId; type WeightInfo = (); type Currency = Currencies; type Control = Control; + #[cfg(feature = "runtime-benchmarks")] + type ControlBenchmarkHelper = Control; type GameDAOTreasury = GameDAOTreasury; type MinNameLength = MinNameLength; type MaxCampaignsPerBlock = MaxCampaignsPerBlock; @@ -222,15 +221,19 @@ parameter_types! { pub const MinProposalDeposit: Balance = 10 * DOLLARS; pub SlashingMajority: Permill = Permill::from_rational(2u32, 3u32); pub GameDAOGetsFromSlashing: Permill = Permill::from_rational(1u32, 10u32); - pub const ProposalDurationLimits: (BlockNumber, BlockNumber) = (10, 100); + pub const ProposalDurationLimits: (BlockNumber, BlockNumber) = (100, 864000); } impl gamedao_signal::Config for Test { - type Event = Event; + type RuntimeEvent = RuntimeEvent; type Balance = Balance; type CurrencyId = CurrencyId; type Currency = Currencies; type Control = Control; type Flow = Flow; + #[cfg(feature = "runtime-benchmarks")] + type ControlBenchmarkHelper = Control; + #[cfg(feature = "runtime-benchmarks")] + type FlowBenchmarkHelper = Flow; type WeightInfo = (); type ProtocolTokenId = ProtocolTokenId; type PaymentTokenId = PaymentTokenId; @@ -244,112 +247,6 @@ impl gamedao_signal::Config for Test { type StringLimit = ConstU32<256>; } -use sp_runtime::traits::{Hash as HashTrait, AccountIdConversion}; -use gamedao_traits::ControlTrait; -use crate::ProposalCount; -use gamedao_control::types::{AccessModel, FeeModel, OrgType, Org}; -use gamedao_flow::{FlowGovernance, FlowProtocol}; -use super::types::{Proposal, ProposalType, SlashingRule}; -use frame_support::assert_ok; -use frame_system::RawOrigin; - -pub fn create_org(members: &Vec) -> (H256, AccountId) { - let bounded_str = BoundedVec::truncate_from(vec![1,2]); - let index = Control::org_count(); - let now = frame_system::Pallet::::block_number(); - let org = Org { - index, creator: ALICE, prime: ALICE, name: bounded_str.clone(), cid: bounded_str.clone(), - org_type: OrgType::Individual, fee_model: FeeModel::NoFees, membership_fee: Some(1 * DOLLARS), - gov_currency: PROTOCOL_TOKEN_ID, pay_currency: PAYMENT_TOKEN_ID, access_model: AccessModel::Open, - member_limit: ::MaxMembers::get(), created: now.clone(), mutated: now - }; - let org_id = ::Hashing::hash_of(&org); - assert_ok!( - Control::create_org( - Origin::signed(ALICE), org.name, org.cid, org.org_type, org.access_model, - org.fee_model, None, org.membership_fee, None, None, None - )); - let treasury_id = Control::org_treasury_account(&org_id).unwrap(); - let init_balance = 100 * DOLLARS; - assert_ok!(Tokens::set_balance(RawOrigin::Root.into(), treasury_id, PROTOCOL_TOKEN_ID, init_balance, 0)); - for x in members { - assert_ok!(Control::add_member(Origin::signed(ALICE), org_id, *x)); - } - (org_id, treasury_id) -} - -pub fn set_balance(accounts: &Vec, amount: Balance) { - for x in accounts { - assert_ok!(Tokens::set_balance(RawOrigin::Root.into(), *x, PROTOCOL_TOKEN_ID, amount, 0)); - assert_ok!(Tokens::set_balance(RawOrigin::Root.into(), *x, PAYMENT_TOKEN_ID, amount, 0)); - } -} - -pub fn create_finalize_campaign( - current_block: BlockNumber, - org_id: H256, - contributors: &Vec, - contribution: Balance, - expiry: BlockNumber, - finalize: bool -) -> H256 { - let index = Flow::campaign_count(); - let bounded_str = BoundedVec::truncate_from(vec![1, 2, 3]); - let campaign = gamedao_flow::types::Campaign { - index, - org_id, - name: bounded_str.clone(), - owner: ALICE, - admin: ALICE, - deposit: 10 * DOLLARS, - start: current_block, - expiry, - cap: 40 * DOLLARS, - protocol: FlowProtocol::default(), - governance: FlowGovernance::default(), - cid: bounded_str.clone(), - token_symbol: None, - token_name: None, - created: current_block, - }; - assert_ok!(Flow::create_campaign( - Origin::signed(ALICE), - org_id, campaign.admin, campaign.name.clone(), campaign.cap, - campaign.deposit, campaign.expiry, campaign.protocol.clone(), - campaign.governance.clone(), campaign.cid.clone(), None, None, None - )); - let campaign_id = ::Hashing::hash_of(&campaign); - for x in contributors { - assert_ok!(Flow::contribute(Origin::signed(*x), campaign_id, contribution)); - } - // Finalize campaign - if finalize { - System::set_block_number(expiry); - Flow::on_finalize(expiry); - System::set_block_number(expiry + 1); - Flow::on_initialize(expiry + 1); - assert_eq!(Flow::is_campaign_succeeded(&campaign_id), true); - } - - campaign_id -} - -pub fn create_proposal( - proposal_type: ProposalType, org_id: H256, start: BlockNumber, expiry: BlockNumber, deposit: Balance, campaign_id: Option, - currency_id: Option, beneficiary: Option, amount: Option -) -> (H256, Proposal::StringLimit>>) { - let bounded_str = BoundedVec::truncate_from(vec![1, 2, 3]); - let proposal = Proposal { - index: >::get(), owner: ALICE, title: bounded_str.clone(), - cid: bounded_str, slashing_rule: SlashingRule::Automated, - start, expiry, org_id, deposit, campaign_id, - amount, beneficiary, proposal_type, currency_id, - }; - let proposal_id: H256 = ::Hashing::hash_of(&proposal); - (proposal_id, proposal) -} - - #[derive(Default)] pub struct ExtBuilder; impl ExtBuilder { diff --git a/signal/src/tests.rs b/signal/src/tests.rs index bd0f34193..33bd33002 100644 --- a/signal/src/tests.rs +++ b/signal/src/tests.rs @@ -1,18 +1,118 @@ #[cfg(test)] use super::{ - mock::{ - BlockNumber, AccountId, Balance, Control, Event, ExtBuilder, - Origin, Signal, System, Test, ALICE, BOB, CHARLIE, DOLLARS, DAYS, - PROTOCOL_TOKEN_ID, PAYMENT_TOKEN_ID, create_proposal, create_finalize_campaign, create_org, set_balance - }, - types::{ProposalType, ProposalState, Majority}, + types::{Proposal, ProposalType, ProposalState, Majority}, *, }; +use crate::mock::{ + BlockNumber, AccountId, Balance, Control, RuntimeEvent as Event, ExtBuilder, Tokens, BoundedString, + RuntimeOrigin as Origin, Signal, System, Test, ALICE, BOB, CHARLIE, DOLLARS, DAYS, + PROTOCOL_TOKEN_ID, PAYMENT_TOKEN_ID, + ProposalDurationLimits, Flow, CurrencyId +}; use frame_system::RawOrigin; use frame_support::{ assert_noop, assert_ok, traits::Hooks }; +use sp_core::H256; +use gamedao_control::types::{AccessModel, FeeModel, OrgType, Org}; +use gamedao_flow::{FlowGovernance, FlowProtocol}; + +pub fn create_org(members: &Vec) -> (H256, AccountId) { + let bounded_str = BoundedVec::truncate_from(vec![1,2]); + let index = Control::org_count(); + let now = frame_system::Pallet::::block_number(); + let org = Org { + index, creator: ALICE, prime: ALICE, name: bounded_str.clone(), cid: bounded_str.clone(), + org_type: OrgType::Individual, fee_model: FeeModel::NoFees, membership_fee: Some(1 * DOLLARS), + gov_currency: PROTOCOL_TOKEN_ID, pay_currency: PAYMENT_TOKEN_ID, access_model: AccessModel::Open, + member_limit: ::MaxMembers::get(), created: now.clone(), mutated: now + }; + let org_id = ::Hashing::hash_of(&org); + assert_ok!( + Control::create_org( + Origin::signed(ALICE), org.name, org.cid, org.org_type, org.access_model, + org.fee_model, None, org.membership_fee, None, None, None + )); + let treasury_id = Control::org_treasury_account(&org_id).unwrap(); + let init_balance = 100 * DOLLARS; + assert_ok!(Tokens::set_balance(RawOrigin::Root.into(), treasury_id, PROTOCOL_TOKEN_ID, init_balance, 0)); + for x in members { + assert_ok!(Control::add_member(Origin::signed(x.clone()), org_id, *x)); + } + (org_id, treasury_id) +} + +pub fn set_balance(accounts: &Vec, amount: Balance) { + for x in accounts { + assert_ok!(Tokens::set_balance(RawOrigin::Root.into(), *x, PROTOCOL_TOKEN_ID, amount, 0)); + assert_ok!(Tokens::set_balance(RawOrigin::Root.into(), *x, PAYMENT_TOKEN_ID, amount, 0)); + } +} + +pub fn create_finalize_campaign( + current_block: BlockNumber, + org_id: H256, + contributors: &Vec, + contribution: Balance, + expiry: BlockNumber, + finalize: bool +) -> H256 { + let index = Flow::campaign_count(); + let bounded_str = BoundedVec::truncate_from(vec![1, 2, 3]); + let campaign = gamedao_flow::types::Campaign { + index, + org_id, + name: bounded_str.clone(), + owner: ALICE, + admin: ALICE, + deposit: 10 * DOLLARS, + start: current_block, + expiry, + cap: 40 * DOLLARS, + protocol: FlowProtocol::default(), + governance: FlowGovernance::default(), + cid: bounded_str.clone(), + token_symbol: None, + token_name: None, + created: current_block, + }; + assert_ok!(Flow::create_campaign( + Origin::signed(ALICE), + org_id, campaign.admin, campaign.name.clone(), campaign.cap, + campaign.deposit, campaign.expiry, campaign.protocol.clone(), + campaign.governance.clone(), campaign.cid.clone(), None, None, None + )); + let campaign_id = ::Hashing::hash_of(&campaign); + for x in contributors { + assert_ok!(Flow::contribute(Origin::signed(*x), campaign_id, contribution)); + } + // Finalize campaign + if finalize { + System::set_block_number(expiry); + Flow::on_finalize(expiry); + System::set_block_number(expiry + 1); + Flow::on_initialize(expiry + 1); + assert_eq!(Flow::is_campaign_succeeded(&campaign_id), true); + } + + campaign_id +} + +pub fn create_proposal( + proposal_type: ProposalType, org_id: H256, start: BlockNumber, expiry: BlockNumber, deposit: Balance, campaign_id: Option, + currency_id: Option, beneficiary: Option, amount: Option +) -> (H256, Proposal) { + let bounded_str = BoundedVec::truncate_from(vec![1, 2, 3]); + let proposal = Proposal { + index: >::get(), owner: ALICE, title: bounded_str.clone(), + cid: bounded_str, slashing_rule: SlashingRule::Automated, + start, expiry, org_id, deposit, campaign_id, + amount, beneficiary, proposal_type, currency_id, + }; + let proposal_id: H256 = ::Hashing::hash_of(&proposal); + (proposal_id, proposal) +} // TODO: more tests for token weighted voting @@ -34,7 +134,7 @@ fn signal_0_0() { let campaign_id = create_finalize_campaign(now, org_id, &contributors, contribution, campaign_expiry, true); let start: BlockNumber = campaign_expiry + 1; - let expiry: BlockNumber = start + 10; + let expiry: BlockNumber = start + ProposalDurationLimits::get().0; let (_, proposal) = create_proposal( ProposalType::Withdrawal, org_id, start, expiry, 20 * DOLLARS, Some(campaign_id), Some(PAYMENT_TOKEN_ID), None, Some(10 * DOLLARS) @@ -181,7 +281,7 @@ fn signal_0_1() { let campaign_id = create_finalize_campaign(now, org_id, &contributors, contribution, campaign_expiry, true); let start: BlockNumber = campaign_expiry + 1; - let expiry: BlockNumber = start + 10; + let expiry: BlockNumber = start + ProposalDurationLimits::get().0; let (_, proposal) = create_proposal( ProposalType::Withdrawal, org_id, start, expiry, 20 * DOLLARS, Some(campaign_id), Some(PAYMENT_TOKEN_ID), None, Some(10 * DOLLARS) @@ -252,17 +352,23 @@ fn signal_0_1() { // CampaignUnsucceeded let now = campaign_expiry + 1; - let campaign_id = create_finalize_campaign(now, org_id, &(51..52).collect(), 50 * DOLLARS, now + 2 * DAYS, false); - // TODO: fix this test - // assert_ok!(Flow::update_state(Origin::signed(ALICE), campaign_id, CampaignStates::Failed)); - // assert_noop!( - // Signal::proposal( - // Origin::signed(ALICE), proposal.proposal_type.clone(), proposal.org_id, - // proposal.title.clone(), proposal.cid.clone(), proposal.expiry, - // Majority::Relative, Unit::Account, Scale::Linear, None, None, None, - // Some(campaign_id), proposal.amount, proposal.beneficiary, proposal.currency_id), - // Error::::CampaignUnsucceeded - // ); + System::set_block_number(now); + let campaign_expiry = now + 2 * DAYS; + let proposal_expiry = campaign_expiry + 1 + ProposalDurationLimits::get().0; + let campaign_id = create_finalize_campaign(now, org_id, &(51..52).collect(), 5 * DOLLARS, campaign_expiry, false); + System::set_block_number(campaign_expiry); + Flow::on_finalize(campaign_expiry); + System::set_block_number(campaign_expiry + 1); + Flow::on_initialize(campaign_expiry + 1); + assert_eq!(Flow::is_campaign_succeeded(&campaign_id), false); + assert_noop!( + Signal::proposal( + Origin::signed(ALICE), proposal.proposal_type.clone(), proposal.org_id, + proposal.title.clone(), proposal.cid.clone(), proposal_expiry, + Majority::Relative, Unit::Account, Scale::Linear, None, None, None, + Some(campaign_id), proposal.amount, proposal.beneficiary, proposal.currency_id), + Error::::CampaignUnsucceeded + ); }); } @@ -284,10 +390,10 @@ fn signal_0_2() { let now: BlockNumber = 3; System::set_block_number(now); let start: BlockNumber = now + 1; - let expiry: BlockNumber = now + 20; + let expiry: BlockNumber = now + 1 + ProposalDurationLimits::get().0; let (proposal_id, proposal) = create_proposal( ProposalType::General, org_id, start, expiry, 20 * DOLLARS, None, None, None, None); - + assert_ok!(Signal::proposal( Origin::signed(ALICE), proposal.proposal_type.clone(), proposal.org_id, proposal.title.clone(), proposal.cid.clone(), proposal.expiry, @@ -316,7 +422,7 @@ fn signal_0_2() { let campaign_expiry = start + 2 * DAYS; let campaign_id = create_finalize_campaign(start, org_id, &contributors, 50 * DOLLARS, campaign_expiry, true); let start: BlockNumber = campaign_expiry + 1; - let expiry: BlockNumber = start + 10; + let expiry: BlockNumber = start + ProposalDurationLimits::get().0; let (proposal_id, proposal) = create_proposal( ProposalType::Withdrawal, org_id, start, expiry, 20 * DOLLARS, Some(campaign_id), Some(PAYMENT_TOKEN_ID), None, Some(withdrawal_amount) @@ -385,7 +491,7 @@ fn signal_1_0() { let now: BlockNumber = 3; System::set_block_number(now); let start: BlockNumber = now + 1; - let expiry: BlockNumber = now + 20; + let expiry: BlockNumber = now + 1 + ProposalDurationLimits::get().0; let total_balance = 100 * DOLLARS - 1 * DOLLARS; // org creation fee let deposit = 20 * DOLLARS; let (proposal_id, proposal) = create_proposal( @@ -404,7 +510,7 @@ fn signal_1_0() { // Check if deposit was reserved assert_eq!(::Currency::total_balance(PROTOCOL_TOKEN_ID, &ALICE), total_balance); assert_eq!(::Currency::free_balance(PROTOCOL_TOKEN_ID, &ALICE), total_balance - deposit); - + System::assert_has_event(Event::Signal(crate::Event::Created { account: ALICE, proposal_id: proposal_id.clone(), @@ -438,7 +544,7 @@ fn signal_1_0() { yes: members.len().saturated_into(), no: 1, })); - + // Hop to the proposal's expiry block and check proposal finalized System::set_block_number(expiry); Signal::on_finalize(expiry); @@ -466,7 +572,7 @@ fn signal_1_1() { set_balance(&members, 100 * DOLLARS); let now: BlockNumber = 3; let start: BlockNumber = now; - let expiry: BlockNumber = now + 10; + let expiry: BlockNumber = now + ProposalDurationLimits::get().0; let total_balance = 100 * DOLLARS - 1 * DOLLARS; // org creation fee let deposit = 20 * DOLLARS; System::set_block_number(now); @@ -496,7 +602,7 @@ fn signal_1_1() { yes: 0, no: (members.len() + 1).saturated_into(), })); - + // Hop to the proposal's expiry block and check proposal finalized System::set_block_number(expiry); Signal::on_finalize(expiry); @@ -508,7 +614,7 @@ fn signal_1_1() { // Check if deposit was slashed assert_eq!(::Currency::free_balance(PROTOCOL_TOKEN_ID, &ALICE), total_balance - deposit); - + // TODO: check balances of GameDAO and Org treasury }); @@ -527,7 +633,7 @@ fn signal_1_2() { set_balance(&members, 100 * DOLLARS); let now: BlockNumber = 3; let start: BlockNumber = now; - let expiry: BlockNumber = now + 10; + let expiry: BlockNumber = now + ProposalDurationLimits::get().0; let total_balance = 100 * DOLLARS - 1 * DOLLARS; // org creation fee let deposit = 20 * DOLLARS; System::set_block_number(now); @@ -569,7 +675,7 @@ fn signal_1_3() { set_balance(&members, 100 * DOLLARS); let now: BlockNumber = 3; let start: BlockNumber = now; - let expiry: BlockNumber = now + 10; + let expiry: BlockNumber = now + ProposalDurationLimits::get().0; let total_balance = 100 * DOLLARS - 1 * DOLLARS; // org creation fee let deposit = 20 * DOLLARS; System::set_block_number(now); @@ -616,7 +722,7 @@ fn signal_1_4() { set_balance(&members, 100 * DOLLARS); let now: BlockNumber = 3; let start: BlockNumber = now; - let expiry: BlockNumber = now + 10; + let expiry: BlockNumber = now + ProposalDurationLimits::get().0; let total_balance = 100 * DOLLARS - 1 * DOLLARS; // org creation fee let deposit = 20 * DOLLARS; System::set_block_number(now); @@ -661,7 +767,7 @@ fn signal_1_5() { set_balance(&members, 100 * DOLLARS); let now: BlockNumber = 3; let start: BlockNumber = now; - let expiry: BlockNumber = now + 10; + let expiry: BlockNumber = now + ProposalDurationLimits::get().0; let total_balance = 100 * DOLLARS - 1 * DOLLARS; // org creation fee let deposit = 20 * DOLLARS; System::set_block_number(now); @@ -711,7 +817,7 @@ fn signal_1_6() { set_balance(&members, 100 * DOLLARS); let now: BlockNumber = 3; let start: BlockNumber = now; - let expiry: BlockNumber = now + 10; + let expiry: BlockNumber = now + ProposalDurationLimits::get().0; let total_balance = 100 * DOLLARS - 1 * DOLLARS; // org creation fee let deposit = 20 * DOLLARS; System::set_block_number(now); @@ -733,7 +839,7 @@ fn signal_1_6() { } // Proposal creator votes "NO" assert_ok!(Signal::vote(Origin::signed(ALICE), proposal_id, false, Some(voting_deposit))); - + // Hop to the proposal's expiry block and check proposal finalized System::set_block_number(expiry); Signal::on_finalize(expiry); @@ -760,7 +866,7 @@ fn signal_1_7() { set_balance(&members, 100 * DOLLARS); let now: BlockNumber = 3; let start: BlockNumber = now; - let expiry: BlockNumber = now + 10; + let expiry: BlockNumber = now + ProposalDurationLimits::get().0; let total_balance = 100 * DOLLARS - 1 * DOLLARS; // org creation fee let deposit = 20 * DOLLARS; System::set_block_number(now); @@ -780,7 +886,7 @@ fn signal_1_7() { } // Proposal creator votes "YES" assert_ok!(Signal::vote(Origin::signed(ALICE), proposal_id, true, None)); - + // Hop to the proposal's expiry block and check proposal finalized System::set_block_number(expiry); Signal::on_finalize(expiry); @@ -809,7 +915,7 @@ fn signal_1_8() { set_balance(&members, 100 * DOLLARS); let now: BlockNumber = 3; let start: BlockNumber = now; - let expiry: BlockNumber = now + 10; + let expiry: BlockNumber = now + ProposalDurationLimits::get().0; let total_balance = 100 * DOLLARS - 1 * DOLLARS; // org creation fee let deposit = 20 * DOLLARS; System::set_block_number(now); @@ -827,7 +933,7 @@ fn signal_1_8() { assert_ok!(Signal::vote(Origin::signed(0), proposal_id, false, None)); // Proposal creator votes "YES" assert_ok!(Signal::vote(Origin::signed(ALICE), proposal_id, true, None)); - + // Hop to the proposal's expiry block and check proposal finalized System::set_block_number(expiry); Signal::on_finalize(expiry); @@ -870,17 +976,17 @@ fn signal_2_0() { let total_balance = 100 * DOLLARS - 1 * DOLLARS; // org creation fee let deposit = 20 * DOLLARS; let withdrawal_amount = 10 * DOLLARS; - + let campaign_expiry = now + 2 * DAYS; let campaign_id = create_finalize_campaign(now, org_id, &contributors, contribution, campaign_expiry, true); - + // Check if campaign was finalized and all treasury balance is reserved assert_eq!(::Currency::total_balance(currency, &treasury_id), total_contribution - commission); assert_eq!(::Currency::free_balance(currency, &treasury_id), 0); let start: BlockNumber = campaign_expiry + 1; - let expiry: BlockNumber = start + 20; + let expiry: BlockNumber = start + ProposalDurationLimits::get().0; let (proposal_id, proposal) = create_proposal( ProposalType::Withdrawal, org_id, start, expiry, deposit, Some(campaign_id), Some(currency), None, Some(withdrawal_amount) @@ -897,7 +1003,7 @@ fn signal_2_0() { for x in &contributors { assert_ok!(Signal::vote(Origin::signed(*x), proposal_id, true, None)); } - + // Check if proposal finalized earlier System::assert_has_event(Event::Signal(crate::Event::Finalized { proposal_id: proposal_id.clone(), @@ -941,7 +1047,7 @@ fn signal_2_0() { for x in &members { assert_ok!(Signal::vote(Origin::signed(*x), proposal_id, true, None)); } - + // Hop to the proposal's expiry block and check proposal finalized System::set_block_number(expiry); Signal::on_finalize(expiry); @@ -988,16 +1094,16 @@ fn signal_2_1() { let total_balance = 100 * DOLLARS - 1 * DOLLARS; // org creation fee let deposit = 20 * DOLLARS; let withdrawal_amount = 10 * DOLLARS; - + let campaign_expiry = now + 2 * DAYS; let campaign_id = create_finalize_campaign(now, org_id, &contributors, contribution, campaign_expiry, true); - + // Check if campaign was finalized and all treasury balance is reserved assert_eq!(::Currency::total_balance(currency, &treasury_id), total_contribution - commission); assert_eq!(::Currency::free_balance(currency, &treasury_id), 0); let start: BlockNumber = campaign_expiry + 1; - let expiry: BlockNumber = start + 20; + let expiry: BlockNumber = start + ProposalDurationLimits::get().0; let (proposal_id, proposal) = create_proposal( ProposalType::Withdrawal, org_id, start, expiry, deposit, Some(campaign_id), Some(currency), None, Some(withdrawal_amount) @@ -1014,7 +1120,7 @@ fn signal_2_1() { for x in &contributors { assert_ok!(Signal::vote(Origin::signed(*x), proposal_id, true, None)); } - + // Check if proposal finalized earlier System::assert_has_event(Event::Signal(crate::Event::Finalized { proposal_id: proposal_id.clone(), @@ -1055,7 +1161,7 @@ fn signal_2_1() { for x in &members { assert_ok!(Signal::vote(Origin::signed(*x), proposal_id, false, None)); } - + // Hop to the proposal's expiry block and check proposal finalized System::set_block_number(expiry); Signal::on_finalize(expiry); @@ -1096,13 +1202,13 @@ fn signal_2_2() { let withdrawal_amount = 10 * DOLLARS; let campaign_expiry = now + 2 * DAYS; let campaign_id = create_finalize_campaign(now, org_id, &contributors, contribution, campaign_expiry, true); - + // Check if campaign was finalized and all treasury balance is reserved assert_eq!(::Currency::total_balance(currency, &treasury_id), total_contribution - commission); assert_eq!(::Currency::free_balance(currency, &treasury_id), 0); let start: BlockNumber = campaign_expiry + 1; - let expiry: BlockNumber = start + 20; + let expiry: BlockNumber = start + ProposalDurationLimits::get().0; let (proposal_id, proposal) = create_proposal( ProposalType::Withdrawal, org_id, start, expiry, deposit, Some(campaign_id), Some(currency), None, Some(withdrawal_amount) @@ -1119,7 +1225,7 @@ fn signal_2_2() { for x in &contributors { assert_ok!(Signal::vote(Origin::signed(*x), proposal_id, false, None)); } - + // Hop to the proposal's expiry block and check proposal finalized System::set_block_number(expiry); Signal::on_finalize(expiry); diff --git a/signal/src/types.rs b/signal/src/types.rs index 35dc52897..b4a9d91cc 100644 --- a/signal/src/types.rs +++ b/signal/src/types.rs @@ -40,14 +40,22 @@ impl Default for SlashingRule { #[derive(Encode, Decode, PartialEq, Clone, TypeInfo, MaxEncodedLen)] #[cfg_attr(feature = "std", derive(Debug))] pub enum ProposalState { - Created = 0, // waiting for start block - Active = 1, // voting is active - Accepted = 2, // voters did approve - Rejected = 3, // voters did not approve - Expired = 4, // ended without votes + /// Waiting for start block + Created = 0, + /// Voting is active + Active = 1, + /// Voters did approve + Accepted = 2, + /// Voters did not approve + Rejected = 3, + /// Ended without votes + Expired = 4, + /// Sudo abort + Aborted = 5, + /// Proposal's action applied + Finalized = 6, + // TODO: Aborted - Aborted = 5, // sudo abort - Finalized = 6, // proposal's action applied } impl Default for ProposalState { fn default() -> Self { diff --git a/signal/src/weights.rs b/signal/src/weights.rs index 2ae7061c5..aef2adc08 100644 --- a/signal/src/weights.rs +++ b/signal/src/weights.rs @@ -1,35 +1,21 @@ -// This file is part of Substrate. - -// Copyright (C) 2021 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. //! Autogenerated weights for gamedao_signal //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-08-02, STEPS: `20`, REPEAT: 10, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 +//! DATE: 2023-04-19, STEPS: `20`, REPEAT: `10`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 // Executed Command: // ./target/release/subzero // benchmark // pallet +// --execution=wasm // --pallet=gamedao_signal // --extrinsic=* // --steps=20 // --repeat=10 -// --output=gamedao-protocol/signal/src/weights.rs +// --output=modules/gamedao-protocol/signal/src/weights.rs // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -49,94 +35,152 @@ pub trait WeightInfo { /// Weights for gamedao_signal using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Control OrgState (r:1 w:0) - // Storage: Control OrgMemberState (r:1 w:0) - // Storage: Flow CampaignOwner (r:1 w:0) - // Storage: Flow CampaignState (r:1 w:0) - // Storage: Signal CampaignBalanceUsed (r:1 w:0) - // Storage: Flow CampaignBalance (r:1 w:0) - // Storage: Signal ProposalCount (r:1 w:1) - // Storage: Signal ProposalOf (r:1 w:1) - // Storage: Tokens Accounts (r:2 w:2) - // Storage: Control OrgTreasury (r:1 w:0) - // Storage: Signal ProposalsByBlock (r:2 w:2) - // Storage: Control OrgMemberCount (r:1 w:0) - // Storage: Signal ProposalVoting (r:0 w:1) - // Storage: Signal ProposalStates (r:0 w:1) + /// Storage: Control OrgStates (r:1 w:0) + /// Proof: Control OrgStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control MemberStates (r:1 w:0) + /// Proof: Control MemberStates (max_values: None, max_size: Some(97), added: 2572, mode: MaxEncodedLen) + /// Storage: Signal ProposalCount (r:1 w:1) + /// Proof: Signal ProposalCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Signal ProposalOf (r:1 w:1) + /// Proof: Signal ProposalOf (max_values: None, max_size: Some(363), added: 2838, mode: MaxEncodedLen) + /// Storage: Tokens Accounts (r:2 w:2) + /// Proof: Tokens Accounts (max_values: None, max_size: Some(109), added: 2584, mode: MaxEncodedLen) + /// Storage: Control OrgTreasury (r:1 w:0) + /// Proof: Control OrgTreasury (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Signal ProposalsByBlock (r:2 w:2) + /// Proof: Signal ProposalsByBlock (max_values: None, max_size: Some(3239), added: 5714, mode: MaxEncodedLen) + /// Storage: Control OrgMemberCount (r:1 w:0) + /// Proof: Control OrgMemberCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: Signal ProposalVoting (r:0 w:1) + /// Proof: Signal ProposalVoting (max_values: None, max_size: Some(130112), added: 132587, mode: MaxEncodedLen) + /// Storage: Signal ProposalStates (r:0 w:1) + /// Proof: Signal ProposalStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn proposal() -> Weight { - (76_000_000 as Weight) - .saturating_add(T::DbWeight::get().reads(14 as Weight)) - .saturating_add(T::DbWeight::get().writes(8 as Weight)) + // Proof Size summary in bytes: + // Measured: `1328` + // Estimated: `38031` + // Minimum execution time: 98_000 nanoseconds. + Weight::from_parts(100_000_000, 38031) + .saturating_add(T::DbWeight::get().reads(10_u64)) + .saturating_add(T::DbWeight::get().writes(8_u64)) } - // Storage: Signal ProposalVoting (r:1 w:1) - // Storage: Signal ProposalOf (r:1 w:0) - // Storage: Control OrgMemberState (r:1 w:0) - // Storage: Signal ProposalStates (r:1 w:1) - // Storage: Control OrgTreasury (r:1 w:0) - // Storage: Tokens Accounts (r:4 w:4) - // Storage: System Account (r:2 w:2) + /// Storage: Signal ProposalVoting (r:1 w:1) + /// Proof: Signal ProposalVoting (max_values: None, max_size: Some(130112), added: 132587, mode: MaxEncodedLen) + /// Storage: Signal ProposalOf (r:1 w:0) + /// Proof: Signal ProposalOf (max_values: None, max_size: Some(363), added: 2838, mode: MaxEncodedLen) + /// Storage: Control MemberStates (r:1 w:0) + /// Proof: Control MemberStates (max_values: None, max_size: Some(97), added: 2572, mode: MaxEncodedLen) + /// Storage: Signal ProposalStates (r:1 w:1) + /// Proof: Signal ProposalStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control OrgTreasury (r:1 w:0) + /// Proof: Control OrgTreasury (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Tokens Accounts (r:3 w:3) + /// Proof: Tokens Accounts (max_values: None, max_size: Some(109), added: 2584, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// The range of component `m` is `[0, 1000]`. fn vote(m: u32, ) -> Weight { - (83_015_000 as Weight) - // Standard Error: 4_000 - .saturating_add((152_000 as Weight).saturating_mul(m as Weight)) - .saturating_add(T::DbWeight::get().reads(11 as Weight)) - .saturating_add(T::DbWeight::get().writes(8 as Weight)) + // Proof Size summary in bytes: + // Measured: `3816 + m * (51 ±0)` + // Estimated: `160361` + // Minimum execution time: 111_000 nanoseconds. + Weight::from_parts(108_350_723, 160361) + // Standard Error: 1_962 + .saturating_add(Weight::from_ref_time(108_429).saturating_mul(m.into())) + .saturating_add(T::DbWeight::get().reads(9_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) } - // Storage: Signal ProposalsByBlock (r:1 w:0) - // Storage: Signal ProposalStates (r:5 w:5) + /// Storage: Signal ProposalsByBlock (r:1 w:0) + /// Proof: Signal ProposalsByBlock (max_values: None, max_size: Some(3239), added: 5714, mode: MaxEncodedLen) + /// Storage: Signal ProposalStates (r:100 w:0) + /// Proof: Signal ProposalStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// The range of component `p` is `[0, 100]`. fn on_initialize(p: u32, ) -> Weight { - (10_472_000 as Weight) - // Standard Error: 18_000 - .saturating_add((5_249_000 as Weight).saturating_mul(p as Weight)) - .saturating_add(T::DbWeight::get().reads(1 as Weight)) - .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(p as Weight))) - .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(p as Weight))) + // Proof Size summary in bytes: + // Measured: `362 + p * (86 ±0)` + // Estimated: `7694 + p * (2524 ±0)` + // Minimum execution time: 4_000 nanoseconds. + Weight::from_parts(9_238_222, 7694) + // Standard Error: 7_826 + .saturating_add(Weight::from_ref_time(3_204_576).saturating_mul(p.into())) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(p.into()))) + .saturating_add(Weight::from_proof_size(2524).saturating_mul(p.into())) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Control OrgState (r:1 w:0) - // Storage: Control OrgMemberState (r:1 w:0) - // Storage: Flow CampaignOwner (r:1 w:0) - // Storage: Flow CampaignState (r:1 w:0) - // Storage: Signal CampaignBalanceUsed (r:1 w:0) - // Storage: Flow CampaignBalance (r:1 w:0) - // Storage: Signal ProposalCount (r:1 w:1) - // Storage: Signal ProposalOf (r:1 w:1) - // Storage: Tokens Accounts (r:2 w:2) - // Storage: Control OrgTreasury (r:1 w:0) - // Storage: Signal ProposalsByBlock (r:2 w:2) - // Storage: Control OrgMemberCount (r:1 w:0) - // Storage: Signal ProposalVoting (r:0 w:1) - // Storage: Signal ProposalStates (r:0 w:1) + /// Storage: Control OrgStates (r:1 w:0) + /// Proof: Control OrgStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control MemberStates (r:1 w:0) + /// Proof: Control MemberStates (max_values: None, max_size: Some(97), added: 2572, mode: MaxEncodedLen) + /// Storage: Signal ProposalCount (r:1 w:1) + /// Proof: Signal ProposalCount (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Signal ProposalOf (r:1 w:1) + /// Proof: Signal ProposalOf (max_values: None, max_size: Some(363), added: 2838, mode: MaxEncodedLen) + /// Storage: Tokens Accounts (r:2 w:2) + /// Proof: Tokens Accounts (max_values: None, max_size: Some(109), added: 2584, mode: MaxEncodedLen) + /// Storage: Control OrgTreasury (r:1 w:0) + /// Proof: Control OrgTreasury (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Signal ProposalsByBlock (r:2 w:2) + /// Proof: Signal ProposalsByBlock (max_values: None, max_size: Some(3239), added: 5714, mode: MaxEncodedLen) + /// Storage: Control OrgMemberCount (r:1 w:0) + /// Proof: Control OrgMemberCount (max_values: None, max_size: Some(52), added: 2527, mode: MaxEncodedLen) + /// Storage: Signal ProposalVoting (r:0 w:1) + /// Proof: Signal ProposalVoting (max_values: None, max_size: Some(130112), added: 132587, mode: MaxEncodedLen) + /// Storage: Signal ProposalStates (r:0 w:1) + /// Proof: Signal ProposalStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) fn proposal() -> Weight { - (76_000_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(14 as Weight)) - .saturating_add(RocksDbWeight::get().writes(8 as Weight)) + // Proof Size summary in bytes: + // Measured: `1328` + // Estimated: `38031` + // Minimum execution time: 98_000 nanoseconds. + Weight::from_parts(100_000_000, 38031) + .saturating_add(RocksDbWeight::get().reads(10_u64)) + .saturating_add(RocksDbWeight::get().writes(8_u64)) } - // Storage: Signal ProposalVoting (r:1 w:1) - // Storage: Signal ProposalOf (r:1 w:0) - // Storage: Control OrgMemberState (r:1 w:0) - // Storage: Signal ProposalStates (r:1 w:1) - // Storage: Control OrgTreasury (r:1 w:0) - // Storage: Tokens Accounts (r:4 w:4) - // Storage: System Account (r:2 w:2) + /// Storage: Signal ProposalVoting (r:1 w:1) + /// Proof: Signal ProposalVoting (max_values: None, max_size: Some(130112), added: 132587, mode: MaxEncodedLen) + /// Storage: Signal ProposalOf (r:1 w:0) + /// Proof: Signal ProposalOf (max_values: None, max_size: Some(363), added: 2838, mode: MaxEncodedLen) + /// Storage: Control MemberStates (r:1 w:0) + /// Proof: Control MemberStates (max_values: None, max_size: Some(97), added: 2572, mode: MaxEncodedLen) + /// Storage: Signal ProposalStates (r:1 w:1) + /// Proof: Signal ProposalStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// Storage: Control OrgTreasury (r:1 w:0) + /// Proof: Control OrgTreasury (max_values: None, max_size: Some(80), added: 2555, mode: MaxEncodedLen) + /// Storage: Tokens Accounts (r:3 w:3) + /// Proof: Tokens Accounts (max_values: None, max_size: Some(109), added: 2584, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// The range of component `m` is `[0, 1000]`. fn vote(m: u32, ) -> Weight { - (83_015_000 as Weight) - // Standard Error: 4_000 - .saturating_add((152_000 as Weight).saturating_mul(m as Weight)) - .saturating_add(RocksDbWeight::get().reads(11 as Weight)) - .saturating_add(RocksDbWeight::get().writes(8 as Weight)) + // Proof Size summary in bytes: + // Measured: `3816 + m * (51 ±0)` + // Estimated: `160361` + // Minimum execution time: 111_000 nanoseconds. + Weight::from_parts(108_350_723, 160361) + // Standard Error: 1_962 + .saturating_add(Weight::from_ref_time(108_429).saturating_mul(m.into())) + .saturating_add(RocksDbWeight::get().reads(9_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) } - // Storage: Signal ProposalsByBlock (r:1 w:0) - // Storage: Signal ProposalStates (r:5 w:5) + /// Storage: Signal ProposalsByBlock (r:1 w:0) + /// Proof: Signal ProposalsByBlock (max_values: None, max_size: Some(3239), added: 5714, mode: MaxEncodedLen) + /// Storage: Signal ProposalStates (r:100 w:0) + /// Proof: Signal ProposalStates (max_values: None, max_size: Some(49), added: 2524, mode: MaxEncodedLen) + /// The range of component `p` is `[0, 100]`. fn on_initialize(p: u32, ) -> Weight { - (10_472_000 as Weight) - // Standard Error: 18_000 - .saturating_add((5_249_000 as Weight).saturating_mul(p as Weight)) - .saturating_add(RocksDbWeight::get().reads(1 as Weight)) - .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(p as Weight))) - .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(p as Weight))) + // Proof Size summary in bytes: + // Measured: `362 + p * (86 ±0)` + // Estimated: `7694 + p * (2524 ±0)` + // Minimum execution time: 4_000 nanoseconds. + Weight::from_parts(9_238_222, 7694) + // Standard Error: 7_826 + .saturating_add(Weight::from_ref_time(3_204_576).saturating_mul(p.into())) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(p.into()))) + .saturating_add(Weight::from_proof_size(2524).saturating_mul(p.into())) } -} +} \ No newline at end of file diff --git a/traits/Cargo.toml b/traits/Cargo.toml index e8939267a..710b0bde1 100644 --- a/traits/Cargo.toml +++ b/traits/Cargo.toml @@ -1,12 +1,6 @@ -# ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -# ███░▄▄▄█░▄▄▀█░▄▀▄░█░▄▄█░▄▀█░▄▄▀█▀▄▄▀██ -# ███░█▄▀█░▀▀░█░█▄█░█░▄▄█░█░█░▀▀░█░██░██ -# ███▄▄▄▄█▄██▄█▄███▄█▄▄▄█▄▄██▄██▄██▄▄███ -# ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ - [package] name = "gamedao-traits" -version = "1.2.0" +version = "1.3.0" authors = ["zero.io","gamedao.co"] edition = "2018" license = "GPL-3.0-or-later" @@ -15,12 +9,12 @@ repository = "https://github.com/gamedaoco/gamedao-protocol" [dependencies] -serde = { version = "1.0.124", optional = true } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } -scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } -frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28", default-features = false } -frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28", default-features = false, optional = true } -sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.28", default-features = false } +serde = { version = "1.0.143", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } +scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } +frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false } +frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false, optional = true } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.43", default-features = false } [features] default = ["std"] @@ -28,6 +22,7 @@ std = [ "codec/std", "serde/std", "scale-info/std", + "frame-benchmarking/std", "frame-support/std", "sp-std/std", ] diff --git a/traits/src/lib.rs b/traits/src/lib.rs index 9ee4daca9..0c2094421 100644 --- a/traits/src/lib.rs +++ b/traits/src/lib.rs @@ -28,17 +28,14 @@ pub trait ControlTrait { fn org_member_count(org_id: &Hash) -> u32; } +#[cfg(feature = "runtime-benchmarks")] pub trait ControlBenchmarkingTrait { /// Helper method to create organization. - /// ** Should be used for benchmarking only!!! ** - #[cfg(feature = "runtime-benchmarks")] fn create_org(caller: AccountId) -> Result; /// Helper method to add accounts to organisation. /// It is assumed those accounts have enough of currency to pay org joining fee. - /// ** Should be used for benchmarking only!!! ** - #[cfg(feature = "runtime-benchmarks")] fn fill_org_with_members(org_id: &Hash, members: Vec) -> Result<(), DispatchError>; } @@ -51,22 +48,16 @@ pub trait FlowTrait { fn campaign_owner(campaign_id: &Hash) -> Option; } -/// ** Should be used for benchmarking only!!! ** +#[cfg(feature = "runtime-benchmarks")] pub trait FlowBenchmarkingTrait { /// Helper method to create campaign - /// ** Should be used for benchmarking only!!! ** - #[cfg(feature = "runtime-benchmarks")] fn create_campaign(caller: &AccountId, org_id: &Hash, start: BlockNumber) -> Result; /// Helper method to fill campaign with contributions /// It is assumed those accounts have enought currency to contribute - /// ** Should be used for benchmarking only!!! ** - #[cfg(feature = "runtime-benchmarks")] fn create_contributions(campaign_id: &Hash, contributors: Vec) -> Result<(), DispatchError>; /// Trigger campaigns finalization by setting block number to specified value and calling appropriate hooks - /// ** Should be used for benchmarking only!!! ** - #[cfg(feature = "runtime-benchmarks")] fn finalize_campaigns_by_block(block_number: BlockNumber); }