From c2cd9ea7463f43f6d9bbd5ac79e89ab943e45280 Mon Sep 17 00:00:00 2001
From: Liam Aharon <liam.aharon@hotmail.com>
Date: Fri, 22 Mar 2024 17:44:49 +0700
Subject: [PATCH] wip staking rewards

---
 Cargo.lock                                 |  20 ++
 Cargo.toml                                 |   1 +
 substrate/frame/staking-rewards/Cargo.toml |  68 ++++++
 substrate/frame/staking-rewards/src/lib.rs | 251 +++++++++++++++++++++
 4 files changed, 340 insertions(+)
 create mode 100644 substrate/frame/staking-rewards/Cargo.toml
 create mode 100644 substrate/frame/staking-rewards/src/lib.rs

diff --git a/Cargo.lock b/Cargo.lock
index bdbf6ddac26..451497f7f5c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -10869,6 +10869,26 @@ dependencies = [
  "sp-arithmetic",
 ]
 
+[[package]]
+name = "pallet-staking-rewards"
+version = "1.0.0"
+dependencies = [
+ "frame-benchmarking",
+ "frame-support",
+ "frame-system",
+ "pallet-assets",
+ "pallet-balances",
+ "parity-scale-codec",
+ "primitive-types",
+ "scale-info",
+ "sp-api",
+ "sp-arithmetic",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
+ "sp-std 14.0.0",
+]
+
 [[package]]
 name = "pallet-staking-runtime-api"
 version = "14.0.0"
diff --git a/Cargo.toml b/Cargo.toml
index 01d6ef8e87b..6bcd830f14c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -381,6 +381,7 @@ members = [
 	"substrate/frame/session/benchmarking",
 	"substrate/frame/society",
 	"substrate/frame/staking",
+	"substrate/frame/staking-rewards",
 	"substrate/frame/staking/reward-curve",
 	"substrate/frame/staking/reward-fn",
 	"substrate/frame/staking/runtime-api",
diff --git a/substrate/frame/staking-rewards/Cargo.toml b/substrate/frame/staking-rewards/Cargo.toml
new file mode 100644
index 00000000000..0a693545f76
--- /dev/null
+++ b/substrate/frame/staking-rewards/Cargo.toml
@@ -0,0 +1,68 @@
+[package]
+name = "pallet-staking-rewards"
+version = "1.0.0"
+authors.workspace = true
+edition.workspace = true
+license = "Apache-2.0"
+homepage = "https://substrate.io"
+repository.workspace = true
+description = "FRAME staking rewards pallet"
+readme = "README.md"
+
+[lints]
+workspace = true
+
+[package.metadata.docs.rs]
+targets = ["x86_64-unknown-linux-gnu"]
+
+[dependencies]
+codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false }
+frame-support = { path = "../support", default-features = false }
+frame-system = { path = "../system", default-features = false }
+frame-benchmarking = { path = "../benchmarking", default-features = false, optional = true }
+scale-info = { version = "2.10.0", default-features = false, features = ["derive"] }
+sp-api = { path = "../../primitives/api", default-features = false }
+sp-core = { path = "../../primitives/core", default-features = false }
+sp-io = { path = "../../primitives/io", default-features = false }
+sp-std = { path = "../../primitives/std", default-features = false }
+sp-runtime = { path = "../../primitives/runtime", default-features = false }
+sp-arithmetic = { path = "../../primitives/arithmetic", default-features = false }
+
+[dev-dependencies]
+pallet-balances = { path = "../balances" }
+pallet-assets = { path = "../assets" }
+primitive-types = { version = "0.12.0", default-features = false, features = ["codec", "num-traits", "scale-info"] }
+
+[features]
+default = ["std"]
+std = [
+	"codec/std",
+	"frame-benchmarking?/std",
+	"frame-support/std",
+	"frame-system/std",
+	"pallet-assets/std",
+	"pallet-balances/std",
+	"primitive-types/std",
+	"scale-info/std",
+	"sp-api/std",
+	"sp-arithmetic/std",
+	"sp-core/std",
+	"sp-io/std",
+	"sp-runtime/std",
+	"sp-std/std",
+]
+runtime-benchmarks = [
+	"frame-benchmarking/runtime-benchmarks",
+	"frame-support/runtime-benchmarks",
+	"frame-system/runtime-benchmarks",
+	"pallet-assets/runtime-benchmarks",
+	"pallet-balances/runtime-benchmarks",
+	"sp-runtime/runtime-benchmarks",
+]
+try-runtime = [
+	"frame-support/try-runtime",
+	"frame-system/try-runtime",
+	"pallet-assets/try-runtime",
+	"pallet-balances/try-runtime",
+	"sp-runtime/try-runtime",
+]
diff --git a/substrate/frame/staking-rewards/src/lib.rs b/substrate/frame/staking-rewards/src/lib.rs
new file mode 100644
index 00000000000..d100e11cba7
--- /dev/null
+++ b/substrate/frame/staking-rewards/src/lib.rs
@@ -0,0 +1,251 @@
+// This file is part of Substrate.
+
+// Copyright (C) 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.
+
+//! # FRAME Staking Rewards Pallet
+//!
+//! A pallet that allows users to stake assets and receive rewards in return.
+//!
+//! Based on the [AccumulatedRewardsPerShare](https://dev.to/heymarkkop/understanding-sushiswaps-masterchef-staking-rewards-1m6f) algorithm.
+#![deny(missing_docs)]
+#![cfg_attr(not(feature = "std"), no_std)]
+
+use codec::{Decode, Encode, MaxEncodedLen};
+use frame_system::pallet_prelude::BlockNumberFor;
+pub use pallet::*;
+
+use frame_support::{
+	traits::{
+		fungibles::{Balanced, Inspect, Mutate},
+		tokens::Balance,
+	},
+	PalletId,
+};
+use scale_info::TypeInfo;
+use sp_core::Get;
+use sp_runtime::{DispatchError, Saturating};
+use sp_std::boxed::Box;
+
+/// Unique identifier for a staking pool. (staking_asset, reward_asset).
+pub type PoolId<AssetId> = (AssetId, AssetId);
+
+/// Information on a user currently staking in a pool.
+#[derive(Decode, Encode, Default, PartialEq, Eq, MaxEncodedLen, TypeInfo)]
+pub struct PoolStakerInfo<Balance> {
+	amount: Balance,
+	rewards: Balance,
+	reward_debt: Balance,
+}
+
+/// Staking pool.
+#[derive(Decode, Encode, Default, PartialEq, Eq, MaxEncodedLen, TypeInfo)]
+pub struct PoolInfo<Balance, BlockNumber> {
+	reward_rate_per_block: Balance,
+	total_tokens_staked: Balance,
+	accumulated_rewards_per_share: Balance,
+	last_rewarded_block: BlockNumber,
+}
+
+#[frame_support::pallet(dev_mode)]
+pub mod pallet {
+	use super::*;
+	use frame_support::pallet_prelude::*;
+	use frame_system::pallet_prelude::*;
+	use sp_runtime::traits::AccountIdConversion;
+
+	#[pallet::pallet]
+	pub struct Pallet<T>(_);
+
+	#[pallet::config]
+	pub trait Config: frame_system::Config {
+		/// Overarching event type.
+		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
+
+		/// The pallet's id, used for deriving its sovereign account ID.
+		#[pallet::constant]
+		type PalletId: Get<PalletId>;
+
+		/// Identifier for each type of asset.
+		type AssetId: Member + Parameter + Clone + MaybeSerializeDeserialize + MaxEncodedLen;
+
+		/// The type in which the assets are measured.
+		type Balance: Balance + TypeInfo;
+
+		/// Registry of assets that can be configured to either stake for rewards, or be offered as
+		/// rewards for staking.
+		type Assets: Inspect<Self::AccountId, AssetId = Self::AssetId, Balance = Self::Balance>
+			+ Mutate<Self::AccountId>
+			+ Balanced<Self::AccountId>;
+	}
+
+	/// State of stakers in each pool.
+	#[pallet::storage]
+	pub type PoolStakers<T: Config> = StorageDoubleMap<
+		_,
+		Blake2_128Concat,
+		PoolId<T::AssetId>,
+		Blake2_128Concat,
+		T::AccountId,
+		PoolStakerInfo<T::Balance>,
+	>;
+
+	/// State and configuraiton of each staking pool.
+	#[pallet::storage]
+	pub type Pools<T: Config> = StorageMap<
+		_,
+		Blake2_128Concat,
+		PoolId<T::AssetId>,
+		PoolInfo<T::Balance, BlockNumberFor<T>>,
+	>;
+
+	#[pallet::event]
+	#[pallet::generate_deposit(pub(super) fn deposit_event)]
+	pub enum Event<T: Config> {
+		/// An account staked some tokens in a pool.
+		Stake {
+			/// The account.
+			who: T::AccountId,
+			/// The pool.
+			pool_id: PoolId<T::AssetId>,
+			/// The amount.
+			amount: T::Balance,
+		},
+		/// An account unstaked some tokens from a pool.
+		Unstake {
+			/// The account.
+			who: T::AccountId,
+			/// The pool.
+			pool_id: PoolId<T::AssetId>,
+			/// The amount.
+			amount: T::Balance,
+		},
+		/// An account harvested some rewards.
+		RewardsHarvested {
+			/// The account.
+			who: T::AccountId,
+			/// The pool.
+			pool_id: PoolId<T::AssetId>,
+			/// The rewarded tokens.
+			amount: T::Balance,
+		},
+		/// A new reward pool was created.
+		PoolCreated {
+			/// The pool.
+			pool_id: PoolId<T::AssetId>,
+			/// The initial reward rate per block.
+			reward_rate_per_block: T::Balance,
+		},
+		/// A reward pool was deleted.
+		PoolDeleted {
+			/// The pool.
+			pool_id: PoolId<T::AssetId>,
+		},
+		/// A pool reward rate was been changed.
+		PoolRewardRateChanged {
+			/// The pool with the changed reward rate.
+			pool_id: PoolId<T::AssetId>,
+			/// The new reward rate of the reward rate per block distributed to stakers.
+			reward_rate_per_block: T::Balance,
+		},
+		/// Funds were withdrawn from the Reward Pool.
+		RewardPoolWithdrawal {
+			/// The asset withdrawn.
+			reward_asset_id: T::AssetId,
+			/// The caller.
+			caller: T::AccountId,
+			/// The acount of reward asset withdrawn.
+			amount: T::Balance,
+		},
+	}
+
+	#[pallet::error]
+	pub enum Error<T> {
+		/// TODO
+		TODO,
+	}
+
+	#[pallet::hooks]
+	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
+		fn integrity_test() {
+			todo!()
+		}
+	}
+
+	/// Pallet's callable functions.
+	#[pallet::call]
+	impl<T: Config> Pallet<T> {
+		/// Create a new reward pool.
+		pub fn create_pool(
+			_origin: OriginFor<T>,
+			_staked_asset_id: T::AssetId,
+			_reward_asset_id: T::AssetId,
+		) -> DispatchResult {
+			todo!()
+		}
+
+		/// Removes an existing reward pool.
+		///
+		/// TODO decide how to manage clean up of stakers from a removed pool
+		pub fn remove_pool(
+			_origin: OriginFor<T>,
+			_staked_asset_id: T::AssetId,
+			_reward_asset_id: T::AssetId,
+		) -> DispatchResult {
+			todo!()
+		}
+
+		/// Stake tokens in a pool.
+		pub fn stake(
+			_origin: OriginFor<T>,
+			_staked_asset_id: T::AssetId,
+			_reward_asset_id: T::AssetId,
+			_amount: T::Balance,
+		) -> DispatchResult {
+			todo!()
+		}
+
+		/// Unstake tokens from a pool.
+		pub fn unstake(
+			_origin: OriginFor<T>,
+			_staked_asset_id: T::AssetId,
+			_reward_asset_id: T::AssetId,
+			_amount: T::Balance,
+		) -> DispatchResult {
+			todo!()
+		}
+
+		/// Harvest unclaimed pool rewards.
+		pub fn harvest_rewards(
+			_origin: OriginFor<T>,
+			_staked_asset_id: T::AssetId,
+			_reward_asset_id: T::AssetId,
+		) -> DispatchResult {
+			todo!()
+		}
+	}
+
+	impl<T: Config> Pallet<T> {
+		/// The account ID of the reward pot.
+		fn reward_pool_account_id() -> T::AccountId {
+			T::PalletId::get().into_account_truncating()
+		}
+
+		/// Update pool state in preparation for reward harvesting.
+		fn update_pool_rewards(_staked_asset_id: T::AssetId, _reward_asset_id: T::AssetId) {
+			todo!()
+		}
+	}
+}
-- 
GitLab