From ec5c010c761cce82f5aef2f76b101948f11c8ca6 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Tue, 27 Aug 2024 10:57:31 +0200 Subject: [PATCH 1/5] initialize rewards module --- Cargo.lock | 19 ++++++++ Cargo.toml | 1 + pallets/rewards/Cargo.toml | 51 ++++++++++++++++++++++ pallets/rewards/src/lib.rs | 89 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 160 insertions(+) create mode 100644 pallets/rewards/Cargo.toml create mode 100644 pallets/rewards/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 90c768cf..0e53472a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7138,6 +7138,25 @@ dependencies = [ "sp-runtime", ] +[[package]] +name = "pallet-rewards" +version = "0.1.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "order-primitives", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "serde", + "smallvec", + "sp-core", + "sp-io", + "sp-runtime", +] + [[package]] name = "pallet-root-testing" version = "4.0.0" diff --git a/Cargo.toml b/Cargo.toml index 0165bfb4..94140451 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -165,3 +165,4 @@ pallet-market = { path = "./pallets/market", default-features = false } pallet-orders = { path = "./pallets/orders", default-features = false } pallet-processor = { path = "./pallets/processor", default-features = false } pallet-regions = { path = "./pallets/regions", default-features = false } +pallet-rewards = { path = "./pallets/rewards", default-features = false } diff --git a/pallets/rewards/Cargo.toml b/pallets/rewards/Cargo.toml new file mode 100644 index 00000000..60c29059 --- /dev/null +++ b/pallets/rewards/Cargo.toml @@ -0,0 +1,51 @@ +[package] +name = "pallet-rewards" +authors = ["Anonymous"] +description = "Pallet for processing coretime orders" +version = "0.1.0" +license = "GPLv3" +edition = "2021" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +log = { workspace = true } +codec = { workspace = true, default-features = false, features = ["derive"] } +scale-info = { workspace = true, default-features = false, features = ["derive"] } + +# Substrate +frame-benchmarking = { workspace = true, default-features = false, optional = true } +frame-support = { workspace = true, default-features = false } +frame-system = { workspace = true, default-features = false } +sp-io = { workspace = true, default-features = false } +sp-core = { workspace = true, default-features = false } +sp-runtime = { workspace = true, default-features = false } + +# Local +order-primitives = { workspace = true, default-features = false } + +[dev-dependencies] +serde = { workspace = true } +smallvec = { workspace = true } +pallet-balances = { workspace = true, default-features = false } + +[features] +default = ["std"] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", +] +std = [ + "log/std", + "codec/std", + "scale-info/std", + "sp-io/std", + "sp-core/std", + "sp-runtime/std", + "frame-benchmarking/std", + "frame-support/std", + "frame-system/std", + "pallet-balances/std", + "order-primitives/std", +] +try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/rewards/src/lib.rs b/pallets/rewards/src/lib.rs new file mode 100644 index 00000000..2dbcc01f --- /dev/null +++ b/pallets/rewards/src/lib.rs @@ -0,0 +1,89 @@ +// This file is part of RegionX. +// +// RegionX is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RegionX is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RegionX. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + +use frame_support::{ + traits::{nonfungible::Transfer, Currency, ExistenceRequirement}, + weights::WeightToFee, +}; +use frame_system::WeightInfo; +use order_primitives::{OrderFactory, OrderId, OrderInspect, ParaId, Requirements}; +pub use pallet::*; +use sp_runtime::traits::Convert; + +const LOG_TARGET: &str = "runtime::rewards"; + +pub type BalanceOf = + <::Currency as Currency<::AccountId>>::Balance; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use frame_support::{ + pallet_prelude::*, + traits::{ + fungible::{Inspect, Mutate}, + nonfungible::Mutate as NftMutate, + tokens::Balance, + ReservableCurrency, + }, + }; + use frame_system::pallet_prelude::*; + + /// The module configuration trait. + #[pallet::config] + pub trait Config: frame_system::Config { + /// The overarching event type. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + /// Currency used for purchasing coretime. + type Currency: Mutate + ReservableCurrency; + + /// Relay chain balance type + type Balance: Balance + + Into<>::Balance> + + Into; + + /// Type over which we can access order data. + type Orders: OrderInspect + OrderFactory; + + /// Weight Info + type WeightInfo: WeightInfo; + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event {} + + #[pallet::error] + #[derive(PartialEq)] + pub enum Error {} + + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + #[pallet::weight(10_000)] + pub fn configure_rewards(origin: OriginFor, order_id: OrderId) -> DispatchResult { + let who = ensure_signed(origin)?; + // TODO + + Ok(()) + } + } +} From e5ac434eb46b60fd8d315731b2f5b669bfcad0f5 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Tue, 27 Aug 2024 17:49:05 +0200 Subject: [PATCH 2/5] configure_rewards --- pallets/rewards/src/lib.rs | 67 +++++++++++++++++++++++++++++------- pallets/rewards/src/types.rs | 26 ++++++++++++++ 2 files changed, 81 insertions(+), 12 deletions(-) create mode 100644 pallets/rewards/src/types.rs diff --git a/pallets/rewards/src/lib.rs b/pallets/rewards/src/lib.rs index 2dbcc01f..3fd173d9 100644 --- a/pallets/rewards/src/lib.rs +++ b/pallets/rewards/src/lib.rs @@ -15,14 +15,13 @@ #![cfg_attr(not(feature = "std"), no_std)] -use frame_support::{ - traits::{nonfungible::Transfer, Currency, ExistenceRequirement}, - weights::WeightToFee, -}; +use frame_support::traits::Currency; use frame_system::WeightInfo; -use order_primitives::{OrderFactory, OrderId, OrderInspect, ParaId, Requirements}; +use order_primitives::{OrderFactory, OrderId, OrderInspect}; pub use pallet::*; -use sp_runtime::traits::Convert; +use sp_runtime::traits::Zero; + +mod types; const LOG_TARGET: &str = "runtime::rewards"; @@ -36,12 +35,12 @@ pub mod pallet { pallet_prelude::*, traits::{ fungible::{Inspect, Mutate}, - nonfungible::Mutate as NftMutate, tokens::Balance, ReservableCurrency, }, }; use frame_system::pallet_prelude::*; + use types::RewardDetails; /// The module configuration trait. #[pallet::config] @@ -49,7 +48,16 @@ pub mod pallet { /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// Currency used for purchasing coretime. + /// The type used as a unique asset id, + type AssetId: Parameter + + Member + + Default + + TypeInfo + + MaybeSerializeDeserialize + + MaxEncodedLen + + Zero; + + /// TODO: remove and replace with a multicurrency type. type Currency: Mutate + ReservableCurrency; /// Relay chain balance type @@ -67,22 +75,57 @@ pub mod pallet { #[pallet::pallet] pub struct Pallet(_); + /// Defines the rewards that will be distributed among order contributors if the order is + /// fulfilled on time. + #[pallet::storage] + #[pallet::getter(fn order_rewards)] + pub type OrderRewards = + StorageMap<_, Blake2_128Concat, OrderId, RewardDetails>>; + #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event {} #[pallet::error] #[derive(PartialEq)] - pub enum Error {} + pub enum Error { + /// The caller submitted an extrinsic which wasn't allowed with their origin. + Unallowed, + /// The order expired and there is no point in configuring rewards. + OrderExpired, + /// Order not found. + UnknownOrder, + /// Rewards can only be configured if there is no existing configuration or if the rewards + /// are not yet set and are currently zero. + CantConfigure, + } #[pallet::call] impl Pallet { + /// The order creator can configure the asset which will be used for rewarding contributors. #[pallet::call_index(0)] #[pallet::weight(10_000)] - pub fn configure_rewards(origin: OriginFor, order_id: OrderId) -> DispatchResult { - let who = ensure_signed(origin)?; - // TODO + pub fn configure_rewards( + origin: OriginFor, + order_id: OrderId, + asset_id: T::AssetId, + ) -> DispatchResult { + let caller = ensure_signed(origin)?; + + let order = T::Orders::order(&order_id).ok_or(Error::::UnknownOrder)?; + + ensure!(!T::Orders::order_expired(&order), Error::::OrderExpired); + ensure!(order.creator == caller, Error::::Unallowed); + + let maybe_rewards = OrderRewards::::get(order_id); + // Rewards can be reconfigured if the amount is still zero. + if let Some(rewards) = maybe_rewards { + ensure!(rewards.amount == Zero::zero(), Error::::CantConfigure); + } + + OrderRewards::::insert(order_id, RewardDetails { asset_id, amount: Zero::zero() }); + // TODO: deposit event Ok(()) } } diff --git a/pallets/rewards/src/types.rs b/pallets/rewards/src/types.rs new file mode 100644 index 00000000..1e228f47 --- /dev/null +++ b/pallets/rewards/src/types.rs @@ -0,0 +1,26 @@ +// This file is part of RegionX. +// +// RegionX is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RegionX is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RegionX. If not, see . + +use codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; + +/// Contains reward details of an order. +#[derive(Encode, Decode, Debug, Clone, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +pub struct RewardDetails { + /// The asset which the contributors will receive, + pub asset_id: AssetId, + /// Amount of tokens that will be distributed to contributors. + pub amount: Balance, +} From cca90bb099e6be0f66203474a15136cc185a192f Mon Sep 17 00:00:00 2001 From: Szegoo Date: Tue, 27 Aug 2024 17:57:40 +0200 Subject: [PATCH 3/5] progress --- pallets/rewards/src/lib.rs | 41 +++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/pallets/rewards/src/lib.rs b/pallets/rewards/src/lib.rs index 3fd173d9..959ac77f 100644 --- a/pallets/rewards/src/lib.rs +++ b/pallets/rewards/src/lib.rs @@ -79,7 +79,7 @@ pub mod pallet { /// fulfilled on time. #[pallet::storage] #[pallet::getter(fn order_rewards)] - pub type OrderRewards = + pub type RewardPools = StorageMap<_, Blake2_128Concat, OrderId, RewardDetails>>; #[pallet::event] @@ -98,6 +98,8 @@ pub mod pallet { /// Rewards can only be configured if there is no existing configuration or if the rewards /// are not yet set and are currently zero. CantConfigure, + /// The reward pool of an order was not found. + RewardPoolNotFound, } #[pallet::call] @@ -117,16 +119,45 @@ pub mod pallet { ensure!(!T::Orders::order_expired(&order), Error::::OrderExpired); ensure!(order.creator == caller, Error::::Unallowed); - let maybe_rewards = OrderRewards::::get(order_id); + let maybe_pool = RewardPools::::get(order_id); // Rewards can be reconfigured if the amount is still zero. - if let Some(rewards) = maybe_rewards { - ensure!(rewards.amount == Zero::zero(), Error::::CantConfigure); + if let Some(pool) = maybe_pool { + ensure!(pool.amount == Zero::zero(), Error::::CantConfigure); } - OrderRewards::::insert(order_id, RewardDetails { asset_id, amount: Zero::zero() }); + RewardPools::::insert(order_id, RewardDetails { asset_id, amount: Zero::zero() }); // TODO: deposit event Ok(()) } + + /// Add rewards to a reward pool of an order. + /// + /// The added rewards will be of the configured asset kind. + /// + /// ## Parameters + /// + /// - `origin`: Signed origin. Can be called by any account. + /// - `order_id`: The order to which the user wants to contribute rewards. There must exist + /// a reward pool for the specified order. + /// -`amount`: number of tokens the user wants to add to the reward pool. + #[pallet::call_index(1)] + #[pallet::weight(10_000)] + pub fn add_rewards( + origin: OriginFor, + order_id: OrderId, + amount: BalanceOf, + ) -> DispatchResult { + let _caller = ensure_signed(origin)?; + + let Some(pool) = RewardPools::::get(order_id) else { + return Err(Error::::RewardPoolNotFound.into()) + }; + + // TODO: check if user has enough tokens + // TODO: contribute to the pool. + + Ok(()) + } } } From 50776aaf3ad10fe4a09289cc76440d147c1a90a3 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Tue, 27 Aug 2024 18:02:01 +0200 Subject: [PATCH 4/5] more interesting naming --- pallets/rewards/src/lib.rs | 16 ++++++++-------- pallets/rewards/src/types.rs | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pallets/rewards/src/lib.rs b/pallets/rewards/src/lib.rs index 959ac77f..8a1e136d 100644 --- a/pallets/rewards/src/lib.rs +++ b/pallets/rewards/src/lib.rs @@ -40,7 +40,7 @@ pub mod pallet { }, }; use frame_system::pallet_prelude::*; - use types::RewardDetails; + use types::PrizePoolDetails; /// The module configuration trait. #[pallet::config] @@ -79,8 +79,8 @@ pub mod pallet { /// fulfilled on time. #[pallet::storage] #[pallet::getter(fn order_rewards)] - pub type RewardPools = - StorageMap<_, Blake2_128Concat, OrderId, RewardDetails>>; + pub type PrizePools = + StorageMap<_, Blake2_128Concat, OrderId, PrizePoolDetails>>; #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] @@ -99,7 +99,7 @@ pub mod pallet { /// are not yet set and are currently zero. CantConfigure, /// The reward pool of an order was not found. - RewardPoolNotFound, + PrizePoolNotFound, } #[pallet::call] @@ -119,13 +119,13 @@ pub mod pallet { ensure!(!T::Orders::order_expired(&order), Error::::OrderExpired); ensure!(order.creator == caller, Error::::Unallowed); - let maybe_pool = RewardPools::::get(order_id); + let maybe_pool = PrizePools::::get(order_id); // Rewards can be reconfigured if the amount is still zero. if let Some(pool) = maybe_pool { ensure!(pool.amount == Zero::zero(), Error::::CantConfigure); } - RewardPools::::insert(order_id, RewardDetails { asset_id, amount: Zero::zero() }); + PrizePools::::insert(order_id, PrizePoolDetails { asset_id, amount: Zero::zero() }); // TODO: deposit event Ok(()) @@ -150,8 +150,8 @@ pub mod pallet { ) -> DispatchResult { let _caller = ensure_signed(origin)?; - let Some(pool) = RewardPools::::get(order_id) else { - return Err(Error::::RewardPoolNotFound.into()) + let Some(pool) = PrizePools::::get(order_id) else { + return Err(Error::::PrizePoolNotFound.into()) }; // TODO: check if user has enough tokens diff --git a/pallets/rewards/src/types.rs b/pallets/rewards/src/types.rs index 1e228f47..3cab3d3c 100644 --- a/pallets/rewards/src/types.rs +++ b/pallets/rewards/src/types.rs @@ -18,7 +18,7 @@ use scale_info::TypeInfo; /// Contains reward details of an order. #[derive(Encode, Decode, Debug, Clone, PartialEq, Eq, TypeInfo, MaxEncodedLen)] -pub struct RewardDetails { +pub struct PrizePoolDetails { /// The asset which the contributors will receive, pub asset_id: AssetId, /// Amount of tokens that will be distributed to contributors. From d522c67c0e5fd71b8dfe45ab7ccf383a6daa81f9 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Tue, 27 Aug 2024 18:21:31 +0200 Subject: [PATCH 5/5] fungibles --- pallets/rewards/src/lib.rs | 42 +++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/pallets/rewards/src/lib.rs b/pallets/rewards/src/lib.rs index 8a1e136d..becbe171 100644 --- a/pallets/rewards/src/lib.rs +++ b/pallets/rewards/src/lib.rs @@ -15,7 +15,7 @@ #![cfg_attr(not(feature = "std"), no_std)] -use frame_support::traits::Currency; +use frame_support::traits::fungibles; use frame_system::WeightInfo; use order_primitives::{OrderFactory, OrderId, OrderInspect}; pub use pallet::*; @@ -25,19 +25,20 @@ mod types; const LOG_TARGET: &str = "runtime::rewards"; -pub type BalanceOf = - <::Currency as Currency<::AccountId>>::Balance; +pub type BalanceOf = <::Assets as fungibles::Inspect< + ::AccountId, +>>::Balance; + +pub type AssetIdOf = <::Assets as fungibles::Inspect< + ::AccountId, +>>::AssetId; #[frame_support::pallet] pub mod pallet { use super::*; use frame_support::{ pallet_prelude::*, - traits::{ - fungible::{Inspect, Mutate}, - tokens::Balance, - ReservableCurrency, - }, + traits::{fungibles::Inspect, tokens::Balance}, }; use frame_system::pallet_prelude::*; use types::PrizePoolDetails; @@ -48,23 +49,14 @@ pub mod pallet { /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// The type used as a unique asset id, - type AssetId: Parameter - + Member - + Default - + TypeInfo - + MaybeSerializeDeserialize - + MaxEncodedLen - + Zero; - - /// TODO: remove and replace with a multicurrency type. - type Currency: Mutate + ReservableCurrency; - /// Relay chain balance type type Balance: Balance - + Into<>::Balance> + + Into<>::Balance> + Into; + /// The type should provide access to assets which can be used as reward. + type Assets: Inspect; + /// Type over which we can access order data. type Orders: OrderInspect + OrderFactory; @@ -80,7 +72,7 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn order_rewards)] pub type PrizePools = - StorageMap<_, Blake2_128Concat, OrderId, PrizePoolDetails>>; + StorageMap<_, Blake2_128Concat, OrderId, PrizePoolDetails, BalanceOf>>; #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] @@ -100,6 +92,8 @@ pub mod pallet { CantConfigure, /// The reward pool of an order was not found. PrizePoolNotFound, + /// The asset the user wanted to use for rewards does not exist. + AssetNotFound, } #[pallet::call] @@ -110,7 +104,7 @@ pub mod pallet { pub fn configure_rewards( origin: OriginFor, order_id: OrderId, - asset_id: T::AssetId, + asset_id: AssetIdOf, ) -> DispatchResult { let caller = ensure_signed(origin)?; @@ -125,6 +119,8 @@ pub mod pallet { ensure!(pool.amount == Zero::zero(), Error::::CantConfigure); } + ensure!(T::Assets::asset_exists(asset_id.clone()) == true, Error::::AssetNotFound); + PrizePools::::insert(order_id, PrizePoolDetails { asset_id, amount: Zero::zero() }); // TODO: deposit event