diff --git a/Cargo.toml b/Cargo.toml
index 18465ec..7c4cabd 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -23,9 +23,11 @@ members = [
"frame/cosmos/x/wasm",
"frame/cosmos/x/wasm/types",
"frame/multimap",
+ "frame/rewards",
"frame/solana",
"frame/solana/runtime-api",
"frame/wtema",
+ "primitives/arithmetic",
"primitives/babel",
"primitives/consensus", # dummy
"primitives/consensus/pow",
@@ -33,6 +35,7 @@ members = [
"primitives/ethereum",
"primitives/multimap",
"primitives/nostr",
+ "primitives/rewards",
"primitives/runtime",
"primitives/solana",
"runtime/common",
@@ -159,6 +162,7 @@ wat = "1.0"
# substrate
sc-client-api = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2409" }
sc-consensus = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2409" }
+frame-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2409", default-features = false }
frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2409", default-features = false }
frame-system = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2409", default-features = false }
pallet-assets = { git = "https://github.com/paritytech/polkadot-sdk", branch = "stable2409", default-features = false }
@@ -198,12 +202,14 @@ nc-consensus = { path = "client/consensus" }
nc-consensus-pow = { path = "client/consensus/pow" }
noir-core-primitives = { path = "core-primitives", default-features = false }
noir-runtime-common = { path = "runtime/common", default-features = false }
+np-arithmetic = { path = "primitives/arithmetic", default-features = false }
np-babel = { path = "primitives/babel", default-features = false }
np-consensus-pow = { path = "primitives/consensus/pow", default-features = false }
np-cosmos = { path = "primitives/cosmos", default-features = false }
np-ethereum = { path = "primitives/ethereum", default-features = false }
np-multimap = { path = "primitives/multimap", default-features = false }
np-nostr = { path = "primitives/nostr", default-features = false }
+np-rewards = { path = "primitives/rewards", default-features = false }
np-runtime = { path = "primitives/runtime", default-features = false }
np-solana = { path = "primitives/solana", default-features = false }
pallet-cosmos = { path = "frame/cosmos", default-features = false }
@@ -216,6 +222,7 @@ pallet-cosmos-x-bank-types = { path = "frame/cosmos/x/bank/types", default-featu
pallet-cosmos-x-wasm = { path = "frame/cosmos/x/wasm", default-features = false }
pallet-cosmos-x-wasm-types = { path = "frame/cosmos/x/wasm/types", default-features = false }
pallet-multimap = { path = "frame/multimap", default-features = false }
+pallet-rewards = { path = "frame/rewards", default-features = false }
pallet-solana = { path = "frame/solana", default-features = false }
pallet-wtema = { path = "frame/wtema", default-features = false }
diff --git a/frame/rewards/Cargo.toml b/frame/rewards/Cargo.toml
new file mode 100644
index 0000000..f84daa5
--- /dev/null
+++ b/frame/rewards/Cargo.toml
@@ -0,0 +1,45 @@
+[package]
+name = "pallet-rewards"
+description = "FRAME rewards for block reward distribution"
+license = "GPL-3.0-or-later"
+authors = { workspace = true }
+version = { workspace = true }
+edition = { workspace = true }
+repository = { workspace = true }
+publish = false
+
+[dependencies]
+frame-benchmarking = { workspace = true, optional = true }
+frame-support = { workspace = true }
+frame-system = { workspace = true }
+np-arithmetic = { workspace = true }
+np-rewards = { workspace = true }
+parity-scale-codec = { workspace = true, features = ["derive"] }
+scale-info = { workspace = true, features = ["derive"] }
+sp-inherents = { workspace = true }
+
+[dev-dependencies]
+pallet-balances = { workspace = true, default-features = true }
+sp-io = { workspace = true, default-features = true }
+
+[features]
+default = ["std"]
+std = [
+ "frame-benchmarking?/std",
+ "frame-support/std",
+ "frame-system/std",
+ "np-arithmetic/std",
+ "np-rewards/std",
+ "parity-scale-codec/std",
+ "scale-info/std",
+ "sp-inherents/std",
+]
+runtime-benchmarks = [
+ "frame-benchmarking/runtime-benchmarks",
+ "frame-support/runtime-benchmarks",
+ "frame-system/runtime-benchmarks",
+]
+try-runtime = [
+ "frame-support/try-runtime",
+ "frame-system/try-runtime",
+]
diff --git a/frame/rewards/src/benchmarking.rs b/frame/rewards/src/benchmarking.rs
new file mode 100644
index 0000000..29440a3
--- /dev/null
+++ b/frame/rewards/src/benchmarking.rs
@@ -0,0 +1,83 @@
+// This file is part of Noir.
+
+// Copyright (C) Haderech Pte. Ltd.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+#![cfg(feature = "runtime-benchmarks")]
+
+use super::*;
+use frame_benchmarking::v2::*;
+use frame_support::{
+ sp_runtime::traits::{Get, One},
+ traits::Hooks,
+};
+use frame_system::RawOrigin;
+
+const SEED: u32 = 0;
+
+#[benchmarks]
+mod benchmarks {
+ use super::*;
+
+ #[benchmark]
+ fn coinbase(n: Linear<1, { T::MaxRewardSplits::get() }>) -> Result<(), BenchmarkError> {
+ type System = frame_system::Pallet;
+
+ let mut reward = T::EmissionSchedule::get();
+ let mut rewards: Vec<(T::AccountId, BalanceOf)> = Vec::new();
+ let payout = T::MinPayout::get();
+ for index in 1..n {
+ reward -= payout;
+ rewards.push((account("miner", index, SEED), payout));
+ }
+ let payout = reward;
+ rewards.push((account("miner", n, SEED), reward));
+
+ #[extrinsic_call]
+ _(RawOrigin::None, rewards.clone());
+
+ assert_eq!(Processed::::get(), true);
+ assert_eq!(Rewards::::get(System::::block_number()), rewards);
+ assert_eq!(RewardLocks::::get(account::("miner", n, SEED)), Some(payout));
+
+ Ok(())
+ }
+
+ #[benchmark]
+ fn on_finalize(n: Linear<1, { T::MaxRewardSplits::get() }>) {
+ type Rewards = Pallet;
+ type System = frame_system::Pallet;
+
+ let number = System::::block_number() + One::one();
+ let mut reward = T::EmissionSchedule::get();
+ let mut rewards: Vec<(T::AccountId, BalanceOf)> = Vec::new();
+ let payout = T::MinPayout::get();
+ for index in 1..n {
+ reward -= payout;
+ rewards.push((account("miner", index, SEED), payout));
+ }
+ rewards.push((account("miner", n, SEED), reward));
+ Rewards::::insert_coinbase(number, rewards);
+
+ #[block]
+ {
+ Rewards::::on_finalize(number);
+ Rewards::::on_initialize(number + T::MaturationTime::get());
+ }
+ }
+
+ impl_benchmark_test_suite!(Rewards, crate::mock::new_test_ext(), crate::mock::Text,);
+}
diff --git a/frame/rewards/src/lib.rs b/frame/rewards/src/lib.rs
new file mode 100644
index 0000000..7511258
--- /dev/null
+++ b/frame/rewards/src/lib.rs
@@ -0,0 +1,296 @@
+// This file is part of Noir.
+
+// Copyright (C) Haderech Pte. Ltd.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+//! Rewards pallet for block reward distribution.
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+extern crate alloc;
+
+mod benchmarking;
+mod mock;
+mod tests;
+pub mod weights;
+pub use weights::WeightInfo;
+
+pub use pallet::*;
+
+use alloc::collections::BTreeMap;
+use frame_support::traits::{Currency, LockIdentifier, LockableCurrency, WithdrawReasons};
+use np_arithmetic::traits::{AtLeast32BitUnsigned, CheckedAdd, SaturatingMulDiv, Zero};
+use np_rewards::{split_reward, InherentError, InherentType, INHERENT_IDENTIFIER};
+use parity_scale_codec::FullCodec;
+use sp_inherents::{InherentData, InherentIdentifier};
+
+const LOCK_IDENTIFIER: LockIdentifier = *b"rewards_";
+
+pub type BalanceOf =
+ <::Currency as Currency<::AccountId>>::Balance;
+
+#[frame_support::pallet]
+pub mod pallet {
+ use super::*;
+ use alloc::vec::Vec;
+ use frame_support::pallet_prelude::*;
+ use frame_system::pallet_prelude::*;
+
+ #[pallet::pallet]
+ pub struct Pallet(_);
+
+ #[pallet::config]
+ pub trait Config: frame_system::Config {
+ /// Coin emission schedule.
+ type EmissionSchedule: Get>;
+
+ /// Currency type of this pallet.
+ type Currency: LockableCurrency;
+
+ /// Minimum payout amount.
+ ///
+ /// This must be greater or equal than existential deposit.
+ type MinPayout: Get>;
+
+ /// Required period that newly minted coins become spendable.
+ #[pallet::constant]
+ type MaturationTime: Get>;
+
+ /// Maximum number of reward splits.
+ #[pallet::constant]
+ type MaxRewardSplits: Get;
+
+ /// Miner's contribution to generate the proof of the block.
+ type Share: FullCodec + Copy + AtLeast32BitUnsigned;
+
+ /// Weight information for extrinsics in this pallet.
+ type WeightInfo: WeightInfo;
+ }
+
+ #[pallet::error]
+ pub enum Error {
+ /// Invalid reward amount.
+ InvalidReward,
+ /// Coinbase contains too many reward splits.
+ TooManyRewardSplits,
+ }
+
+ #[pallet::storage]
+ pub type Processed = StorageValue<_, bool, ValueQuery>;
+
+ #[pallet::storage]
+ #[pallet::getter(fn rewards)]
+ pub type Rewards = StorageMap<
+ _,
+ Twox64Concat,
+ BlockNumberFor,
+ BoundedVec<(T::AccountId, BalanceOf), T::MaxRewardSplits>,
+ ValueQuery,
+ >;
+
+ #[pallet::storage]
+ #[pallet::getter(fn reward_locks)]
+ pub type RewardLocks = StorageMap<_, Blake2_128Concat, T::AccountId, BalanceOf>;
+
+ #[pallet::call]
+ impl Pallet {
+ #[pallet::call_index(0)]
+ #[pallet::weight(T::WeightInfo::coinbase(rewards.len() as u32))]
+ pub fn coinbase(
+ origin: OriginFor,
+ rewards: Vec<(T::AccountId, BalanceOf)>,
+ ) -> DispatchResult {
+ ensure_none(origin)?;
+ ensure!(!Processed::::exists(), "multiple coinbase not allowed");
+ ensure!(
+ rewards.len() <= T::MaxRewardSplits::get() as usize,
+ Error::::TooManyRewardSplits
+ );
+
+ let reward = T::EmissionSchedule::get();
+ let mut reward_given = BalanceOf::::zero();
+ for (dest, value) in &rewards {
+ drop(T::Currency::deposit_creating(dest, *value));
+ reward_given += *value;
+
+ RewardLocks::::mutate(dest, |lock| {
+ let new_lock = match lock.take() {
+ Some(lock) => lock + *value,
+ None => *value,
+ };
+ T::Currency::set_lock(
+ LOCK_IDENTIFIER,
+ dest,
+ new_lock,
+ WithdrawReasons::except(WithdrawReasons::TRANSACTION_PAYMENT),
+ );
+ *lock = Some(new_lock);
+ });
+ }
+ ensure!(reward_given == reward, Error::::InvalidReward);
+
+ Rewards::::insert(
+ frame_system::Pallet::::block_number(),
+ BoundedVec::<_, T::MaxRewardSplits>::try_from(rewards).unwrap(),
+ );
+
+ Processed::::put(true);
+ Ok(())
+ }
+ }
+
+ #[pallet::hooks]
+ impl Hooks> for Pallet {
+ fn on_initialize(number: BlockNumberFor) -> Weight {
+ let reward_splits = if number > T::MaturationTime::get() {
+ {
+ let unlocked_number = number - T::MaturationTime::get();
+
+ let rewards = Rewards::::take(unlocked_number);
+ let reward_splits = rewards.len() as u64;
+
+ for (dest, value) in rewards {
+ RewardLocks::::mutate(&dest, |lock| {
+ let locked = lock.unwrap();
+ if locked > value {
+ T::Currency::set_lock(
+ LOCK_IDENTIFIER,
+ &dest,
+ locked - value,
+ WithdrawReasons::except(WithdrawReasons::TRANSACTION_PAYMENT),
+ );
+ *lock = Some(locked - value);
+ } else {
+ T::Currency::remove_lock(LOCK_IDENTIFIER, &dest);
+ *lock = None;
+ }
+ });
+ }
+ reward_splits
+ }
+ } else {
+ 0
+ };
+
+ T::WeightInfo::on_finalize(reward_splits as u32)
+ }
+
+ fn on_finalize(_: BlockNumberFor) {
+ assert!(Processed::::take(), "coinbase must be processed");
+ }
+ }
+
+ #[pallet::inherent]
+ impl ProvideInherent for Pallet
+ where
+ BalanceOf: SaturatingMulDiv,
+ {
+ type Call = Call;
+ type Error = InherentError;
+ const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER;
+
+ fn create_inherent(data: &InherentData) -> Option {
+ let mut shares = data
+ .get_data::>(&INHERENT_IDENTIFIER)
+ .expect("Rewards inherent data not correctly encoded")
+ .expect("Rewards inherent data must be provided");
+
+ let reward = T::EmissionSchedule::get();
+
+ // Prune zero shares and ensure the number of shares is within the limit.
+ shares.retain(|_, share| !share.is_zero());
+ if shares.is_empty() || shares.len() > T::MaxRewardSplits::get() as usize {
+ return None;
+ }
+
+ let mut rewards = split_reward(reward, shares.clone())?;
+
+ // Prune rewards that are below the minimum payout.
+ rewards.retain(|(_, value)| *value >= T::MinPayout::get());
+
+ let rewards = if rewards.len() != shares.len() {
+ // Redistribute the reward to the remaining accounts.
+ shares.retain(|acc, _| rewards.iter().any(|(a, _)| a == acc));
+ split_reward(reward, shares)?
+ } else {
+ rewards
+ };
+
+ if rewards
+ .iter()
+ .try_fold(BalanceOf::::zero(), |sum, (_, value)| sum.checked_add(value))? !=
+ reward
+ {
+ return None
+ }
+
+ Some(Call::coinbase { rewards })
+ }
+
+ fn check_inherent(call: &Self::Call, _data: &InherentData) -> Result<(), Self::Error> {
+ if let Call::coinbase { rewards } = call {
+ let expected_reward = T::EmissionSchedule::get();
+ let reward = rewards
+ .iter()
+ .try_fold(BalanceOf::::zero(), |sum, (_, value)| sum.checked_add(value))
+ .ok_or(InherentError::InvalidReward)?;
+ if reward != expected_reward {
+ return Err(InherentError::InvalidReward);
+ }
+ if BTreeMap::from_iter(rewards.iter().cloned()).len() != rewards.len() {
+ return Err(InherentError::DuplicateBeneficiary);
+ }
+ }
+
+ Ok(())
+ }
+
+ fn is_inherent(call: &Self::Call) -> bool {
+ matches!(call, Call::coinbase { .. })
+ }
+ }
+
+ impl Pallet {
+ /// Pushes the coinbase rewards. Only use for tests.
+ #[cfg(any(feature = "runtime-benchmarks", feature = "std"))]
+ pub fn insert_coinbase(
+ number: BlockNumberFor,
+ rewards: Vec<(T::AccountId, BalanceOf)>,
+ ) {
+ for (dest, value) in &rewards {
+ drop(T::Currency::deposit_creating(dest, *value));
+ RewardLocks::::mutate(dest, |lock| {
+ let new_lock = match lock.take() {
+ Some(lock) => lock + *value,
+ None => *value,
+ };
+ T::Currency::set_lock(
+ LOCK_IDENTIFIER,
+ dest,
+ new_lock,
+ WithdrawReasons::except(WithdrawReasons::TRANSACTION_PAYMENT),
+ );
+ *lock = Some(new_lock);
+ });
+ }
+ Rewards::::insert(
+ number,
+ BoundedVec::<_, T::MaxRewardSplits>::try_from(rewards).unwrap(),
+ );
+ Processed::::put(true);
+ }
+ }
+}
diff --git a/frame/rewards/src/mock.rs b/frame/rewards/src/mock.rs
new file mode 100644
index 0000000..7f57f11
--- /dev/null
+++ b/frame/rewards/src/mock.rs
@@ -0,0 +1,81 @@
+// This file is part of Noir.
+
+// Copyright (C) Haderech Pte. Ltd.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+#![cfg(test)]
+
+use crate as pallet_rewards;
+
+use super::*;
+use frame_support::{
+ derive_impl,
+ sp_runtime::BuildStorage,
+ traits::{ConstU32, ConstU64},
+};
+use sp_io::TestExternalities;
+
+#[frame_support::runtime]
+mod runtime {
+ #[runtime::runtime]
+ #[runtime::derive(
+ RuntimeCall,
+ RuntimeEvent,
+ RuntimeError,
+ RuntimeHoldReason,
+ RuntimeFreezeReason,
+ RuntimeOrigin,
+ RuntimeTask
+ )]
+ pub struct Test;
+
+ #[runtime::pallet_index(0)]
+ pub type System = frame_system;
+
+ #[runtime::pallet_index(1)]
+ pub type Rewards = pallet_rewards;
+
+ #[runtime::pallet_index(2)]
+ pub type Balances = pallet_balances;
+}
+
+type Block = frame_system::mocking::MockBlock;
+
+#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
+impl frame_system::Config for Test {
+ type Block = Block;
+ type AccountData = pallet_balances::AccountData;
+}
+
+#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)]
+impl pallet_balances::Config for Test {
+ type AccountStore = System;
+}
+
+impl Config for Test {
+ type EmissionSchedule = ConstU64<100>;
+ type Currency = Balances;
+ type MinPayout = ConstU64<1>;
+ type MaturationTime = ConstU64<1>;
+ type MaxRewardSplits = ConstU32<100>;
+ type Share = u128;
+ type WeightInfo = ();
+}
+
+pub fn new_test_ext() -> TestExternalities {
+ let t = frame_system::GenesisConfig::::default().build_storage().unwrap();
+ TestExternalities::new(t)
+}
diff --git a/frame/rewards/src/tests.rs b/frame/rewards/src/tests.rs
new file mode 100644
index 0000000..1f0a9d0
--- /dev/null
+++ b/frame/rewards/src/tests.rs
@@ -0,0 +1,42 @@
+// This file is part of Noir.
+
+// Copyright (C) Haderech Pte. Ltd.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+#![cfg(test)]
+
+use super::{mock::*, Error};
+use frame_support::{assert_noop, assert_ok};
+
+#[test]
+fn coinbase_should_work() {
+ new_test_ext().execute_with(|| {
+ assert_noop!(
+ Rewards::coinbase(RuntimeOrigin::none(), vec![(0, 80), (1, 21)]),
+ Error::::InvalidReward
+ );
+ assert_ok!(Rewards::coinbase(RuntimeOrigin::none(), vec![(0, 80), (1, 20)]));
+ });
+}
+
+#[test]
+#[should_panic(expected = "multiple coinbase not allowed")]
+fn multiple_coinbase_should_fail() {
+ new_test_ext().execute_with(|| {
+ Rewards::insert_coinbase(0, vec![(0, 100)]);
+ assert_ok!(Rewards::coinbase(RuntimeOrigin::none(), vec![(0, 100)]));
+ });
+}
diff --git a/frame/rewards/src/weights.rs b/frame/rewards/src/weights.rs
new file mode 100644
index 0000000..8be4b92
--- /dev/null
+++ b/frame/rewards/src/weights.rs
@@ -0,0 +1,173 @@
+// This file is part of Noir.
+
+// Copyright (C) Haderech Pte. Ltd.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+// This program 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.
+//
+// This program 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 this program. If not, see .
+
+//! Autogenerated weights for `pallet_rewards`
+//!
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0
+//! DATE: 2024-04-18, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! WORST CASE MAP SIZE: `1000000`
+//! HOSTNAME: `benchmarks`, CPU: `AMD Ryzen 9 7950X 16-Core Processor`
+//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: 1024
+
+// Executed Command:
+// ./target/release/noir
+// benchmark
+// pallet
+// --pallet
+// pallet_rewards
+// --extrinsic
+// *
+// --output
+// weights.rs
+// --default-pov-mode
+// measured
+
+#![cfg_attr(rustfmt, rustfmt_skip)]
+#![allow(unused_parens)]
+#![allow(unused_imports)]
+#![allow(missing_docs)]
+
+use core::marker::PhantomData;
+use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
+
+pub trait WeightInfo {
+ fn coinbase(n: u32) -> Weight;
+ fn on_finalize(n: u32) -> Weight;
+}
+
+pub struct SubstrateWeight(PhantomData);
+impl WeightInfo for SubstrateWeight
+where
+ T: frame_system::Config,
+{
+ /// Storage: `Rewards::Processed` (r:1 w:1)
+ /// Proof: `Rewards::Processed` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `Measured`)
+ /// Storage: `System::Account` (r:8192 w:8192)
+ /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`)
+ /// Storage: `Rewards::RewardLocks` (r:8192 w:8192)
+ /// Proof: `Rewards::RewardLocks` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `Measured`)
+ /// Storage: `Balances::Locks` (r:8192 w:8192)
+ /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `Measured`)
+ /// Storage: `Balances::Freezes` (r:8192 w:0)
+ /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(185), added: 2660, mode: `Measured`)
+ /// Storage: `Rewards::Rewards` (r:0 w:1)
+ /// Proof: `Rewards::Rewards` (`max_values`: None, `max_size`: Some(393230), added: 395705, mode: `Measured`)
+ /// The range of component `n` is `[1, 8192]`.
+ fn coinbase(n: u32, ) -> Weight {
+ // Proof Size summary in bytes:
+ // Measured: `6`
+ // Estimated: `1491 + n * (2475 ±0)`
+ // Minimum execution time: 34_385_000 picoseconds.
+ Weight::from_parts(34_775_000, 0)
+ .saturating_add(Weight::from_parts(0, 1491))
+ // Standard Error: 29_018
+ .saturating_add(Weight::from_parts(26_542_025, 0).saturating_mul(n.into()))
+ .saturating_add(T::DbWeight::get().reads(1))
+ .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into())))
+ .saturating_add(T::DbWeight::get().writes(2))
+ .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(n.into())))
+ .saturating_add(Weight::from_parts(0, 2475).saturating_mul(n.into()))
+ }
+ /// Storage: `Rewards::Processed` (r:1 w:1)
+ /// Proof: `Rewards::Processed` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `Measured`)
+ /// Storage: `Rewards::Rewards` (r:1 w:1)
+ /// Proof: `Rewards::Rewards` (`max_values`: None, `max_size`: Some(393230), added: 395705, mode: `Measured`)
+ /// Storage: `Rewards::RewardLocks` (r:8192 w:8192)
+ /// Proof: `Rewards::RewardLocks` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `Measured`)
+ /// Storage: `Balances::Locks` (r:8192 w:8192)
+ /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `Measured`)
+ /// Storage: `Balances::Freezes` (r:8192 w:0)
+ /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(185), added: 2660, mode: `Measured`)
+ /// Storage: `System::Account` (r:8192 w:8192)
+ /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`)
+ /// The range of component `n` is `[1, 8192]`.
+ fn on_finalize(n: u32, ) -> Weight {
+ // Proof Size summary in bytes:
+ // Measured: `144 + n * (333 ±0)`
+ // Estimated: `3670 + n * (2809 ±0)`
+ // Minimum execution time: 21_000_000 picoseconds.
+ Weight::from_parts(21_420_000, 0)
+ .saturating_add(Weight::from_parts(0, 3670))
+ // Standard Error: 30_046
+ .saturating_add(Weight::from_parts(22_389_152, 0).saturating_mul(n.into()))
+ .saturating_add(T::DbWeight::get().reads(2))
+ .saturating_add(T::DbWeight::get().reads((4_u64).saturating_mul(n.into())))
+ .saturating_add(T::DbWeight::get().writes(2))
+ .saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(n.into())))
+ .saturating_add(Weight::from_parts(0, 2809).saturating_mul(n.into()))
+ }
+}
+
+impl WeightInfo for () {
+ /// Storage: `Rewards::Processed` (r:1 w:1)
+ /// Proof: `Rewards::Processed` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `Measured`)
+ /// Storage: `System::Account` (r:8192 w:8192)
+ /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`)
+ /// Storage: `Rewards::RewardLocks` (r:8192 w:8192)
+ /// Proof: `Rewards::RewardLocks` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `Measured`)
+ /// Storage: `Balances::Locks` (r:8192 w:8192)
+ /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `Measured`)
+ /// Storage: `Balances::Freezes` (r:8192 w:0)
+ /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(185), added: 2660, mode: `Measured`)
+ /// Storage: `Rewards::Rewards` (r:0 w:1)
+ /// Proof: `Rewards::Rewards` (`max_values`: None, `max_size`: Some(393230), added: 395705, mode: `Measured`)
+ /// The range of component `n` is `[1, 8192]`.
+ fn coinbase(n: u32, ) -> Weight {
+ // Proof Size summary in bytes:
+ // Measured: `6`
+ // Estimated: `1491 + n * (2475 ±0)`
+ // Minimum execution time: 34_385_000 picoseconds.
+ Weight::from_parts(34_775_000, 0)
+ .saturating_add(Weight::from_parts(0, 1491))
+ // Standard Error: 29_018
+ .saturating_add(Weight::from_parts(26_542_025, 0).saturating_mul(n.into()))
+ .saturating_add(RocksDbWeight::get().reads(1))
+ .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(n.into())))
+ .saturating_add(RocksDbWeight::get().writes(2))
+ .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(n.into())))
+ .saturating_add(Weight::from_parts(0, 2475).saturating_mul(n.into()))
+ }
+ /// Storage: `Rewards::Processed` (r:1 w:1)
+ /// Proof: `Rewards::Processed` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `Measured`)
+ /// Storage: `Rewards::Rewards` (r:1 w:1)
+ /// Proof: `Rewards::Rewards` (`max_values`: None, `max_size`: Some(393230), added: 395705, mode: `Measured`)
+ /// Storage: `Rewards::RewardLocks` (r:8192 w:8192)
+ /// Proof: `Rewards::RewardLocks` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `Measured`)
+ /// Storage: `Balances::Locks` (r:8192 w:8192)
+ /// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `Measured`)
+ /// Storage: `Balances::Freezes` (r:8192 w:0)
+ /// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(185), added: 2660, mode: `Measured`)
+ /// Storage: `System::Account` (r:8192 w:8192)
+ /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`)
+ /// The range of component `n` is `[1, 8192]`.
+ fn on_finalize(n: u32, ) -> Weight {
+ // Proof Size summary in bytes:
+ // Measured: `144 + n * (333 ±0)`
+ // Estimated: `3670 + n * (2809 ±0)`
+ // Minimum execution time: 21_000_000 picoseconds.
+ Weight::from_parts(21_420_000, 0)
+ .saturating_add(Weight::from_parts(0, 3670))
+ // Standard Error: 30_046
+ .saturating_add(Weight::from_parts(22_389_152, 0).saturating_mul(n.into()))
+ .saturating_add(RocksDbWeight::get().reads(2))
+ .saturating_add(RocksDbWeight::get().reads((4_u64).saturating_mul(n.into())))
+ .saturating_add(RocksDbWeight::get().writes(2))
+ .saturating_add(RocksDbWeight::get().writes((3_u64).saturating_mul(n.into())))
+ .saturating_add(Weight::from_parts(0, 2809).saturating_mul(n.into()))
+ }
+}
diff --git a/primitives/arithmetic/Cargo.toml b/primitives/arithmetic/Cargo.toml
new file mode 100644
index 0000000..ca93b79
--- /dev/null
+++ b/primitives/arithmetic/Cargo.toml
@@ -0,0 +1,20 @@
+[package]
+name = "np-arithmetic"
+description = "Noir arithmetic primitives"
+license = "Apache-2.0"
+authors = { workspace = true }
+version = { workspace = true }
+edition = { workspace = true }
+repository = { workspace = true }
+publish = false
+
+[dependencies]
+sp-arithmetic = { workspace = true }
+sp-core = { workspace = true }
+
+[features]
+default = ["std"]
+std = [
+ "sp-arithmetic/std",
+ "sp-core/std",
+]
diff --git a/primitives/arithmetic/src/lib.rs b/primitives/arithmetic/src/lib.rs
new file mode 100644
index 0000000..83dec6e
--- /dev/null
+++ b/primitives/arithmetic/src/lib.rs
@@ -0,0 +1,23 @@
+// This file is part of Noir.
+
+// Copyright (C) Haderech Pte. 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.
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+pub use sp_arithmetic::*;
+pub use sp_core::{U256, U512};
+
+pub mod traits;
diff --git a/primitives/arithmetic/src/traits.rs b/primitives/arithmetic/src/traits.rs
new file mode 100644
index 0000000..48dee92
--- /dev/null
+++ b/primitives/arithmetic/src/traits.rs
@@ -0,0 +1,180 @@
+// This file is part of Noir.
+
+// Copyright (C) Haderech Pte. 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.
+
+pub use sp_arithmetic::traits::*;
+
+use core::ops::{Div, Mul};
+use sp_core::{U256, U512};
+
+/// Trait for multiplication and division with saturating.
+pub trait SaturatingMulDiv {
+ /// Calculates `(self * num) / denom` with saturating.
+ fn saturating_mul_div(self, num: Rhs, denom: Rhs) -> Self;
+}
+
+type Promoted = >::Output;
+type WidenPromoted = as Widen>::Output;
+
+impl SaturatingMulDiv for T
+where
+ T: Promotion + UpperBounded,
+ Promoted: Widen,
+ WidenPromoted:
+ Mul