diff --git a/Cargo.lock b/Cargo.lock
index e2cf44a10a..206e451ab5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3514,7 +3514,7 @@ dependencies = [
[[package]]
name = "fc-consensus"
version = "2.0.0-dev"
-source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#46cd85967849be241dac2bb72f561b942a463729"
+source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#a5481542518ec420352d263adcb2f78835ac9bc2"
dependencies = [
"async-trait",
"fp-consensus",
@@ -3530,7 +3530,7 @@ dependencies = [
[[package]]
name = "fc-db"
version = "2.0.0-dev"
-source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#46cd85967849be241dac2bb72f561b942a463729"
+source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#a5481542518ec420352d263adcb2f78835ac9bc2"
dependencies = [
"async-trait",
"fp-storage",
@@ -3550,7 +3550,7 @@ dependencies = [
[[package]]
name = "fc-mapping-sync"
version = "2.0.0-dev"
-source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#46cd85967849be241dac2bb72f561b942a463729"
+source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#a5481542518ec420352d263adcb2f78835ac9bc2"
dependencies = [
"fc-db",
"fc-storage",
@@ -3571,7 +3571,7 @@ dependencies = [
[[package]]
name = "fc-rpc"
version = "2.0.0-dev"
-source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#46cd85967849be241dac2bb72f561b942a463729"
+source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#a5481542518ec420352d263adcb2f78835ac9bc2"
dependencies = [
"ethereum",
"ethereum-types",
@@ -3621,7 +3621,7 @@ dependencies = [
[[package]]
name = "fc-rpc-core"
version = "1.1.0-dev"
-source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#46cd85967849be241dac2bb72f561b942a463729"
+source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#a5481542518ec420352d263adcb2f78835ac9bc2"
dependencies = [
"ethereum",
"ethereum-types",
@@ -3634,7 +3634,7 @@ dependencies = [
[[package]]
name = "fc-storage"
version = "1.0.0-dev"
-source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#46cd85967849be241dac2bb72f561b942a463729"
+source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#a5481542518ec420352d263adcb2f78835ac9bc2"
dependencies = [
"ethereum",
"ethereum-types",
@@ -3786,7 +3786,7 @@ dependencies = [
[[package]]
name = "fp-account"
version = "1.0.0-dev"
-source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#46cd85967849be241dac2bb72f561b942a463729"
+source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#a5481542518ec420352d263adcb2f78835ac9bc2"
dependencies = [
"hex",
"impl-serde",
@@ -3805,7 +3805,7 @@ dependencies = [
[[package]]
name = "fp-consensus"
version = "2.0.0-dev"
-source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#46cd85967849be241dac2bb72f561b942a463729"
+source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#a5481542518ec420352d263adcb2f78835ac9bc2"
dependencies = [
"ethereum",
"parity-scale-codec",
@@ -3817,7 +3817,7 @@ dependencies = [
[[package]]
name = "fp-ethereum"
version = "1.0.0-dev"
-source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#46cd85967849be241dac2bb72f561b942a463729"
+source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#a5481542518ec420352d263adcb2f78835ac9bc2"
dependencies = [
"ethereum",
"ethereum-types",
@@ -3831,7 +3831,7 @@ dependencies = [
[[package]]
name = "fp-evm"
version = "3.0.0-dev"
-source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#46cd85967849be241dac2bb72f561b942a463729"
+source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#a5481542518ec420352d263adcb2f78835ac9bc2"
dependencies = [
"evm",
"frame-support",
@@ -3846,7 +3846,7 @@ dependencies = [
[[package]]
name = "fp-rpc"
version = "3.0.0-dev"
-source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#46cd85967849be241dac2bb72f561b942a463729"
+source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#a5481542518ec420352d263adcb2f78835ac9bc2"
dependencies = [
"ethereum",
"ethereum-types",
@@ -3863,7 +3863,7 @@ dependencies = [
[[package]]
name = "fp-self-contained"
version = "1.0.0-dev"
-source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#46cd85967849be241dac2bb72f561b942a463729"
+source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#a5481542518ec420352d263adcb2f78835ac9bc2"
dependencies = [
"frame-support",
"parity-scale-codec",
@@ -3875,7 +3875,7 @@ dependencies = [
[[package]]
name = "fp-storage"
version = "2.0.0"
-source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#46cd85967849be241dac2bb72f561b942a463729"
+source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#a5481542518ec420352d263adcb2f78835ac9bc2"
dependencies = [
"parity-scale-codec",
"serde",
@@ -6005,7 +6005,7 @@ checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0"
[[package]]
name = "local-runtime"
-version = "5.23.0"
+version = "5.24.0"
dependencies = [
"array-bytes 6.1.0",
"astar-primitives",
@@ -6025,7 +6025,7 @@ dependencies = [
"pallet-assets",
"pallet-aura",
"pallet-balances",
- "pallet-block-reward",
+ "pallet-block-rewards-hybrid",
"pallet-chain-extension-assets",
"pallet-chain-extension-dapps-staking",
"pallet-chain-extension-unified-accounts",
@@ -7381,7 +7381,7 @@ dependencies = [
[[package]]
name = "pallet-base-fee"
version = "1.0.0"
-source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#46cd85967849be241dac2bb72f561b942a463729"
+source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#a5481542518ec420352d263adcb2f78835ac9bc2"
dependencies = [
"fp-evm",
"frame-support",
@@ -7454,6 +7454,25 @@ dependencies = [
"sp-std",
]
+[[package]]
+name = "pallet-block-rewards-hybrid"
+version = "0.1.0"
+dependencies = [
+ "astar-primitives",
+ "frame-benchmarking",
+ "frame-support",
+ "frame-system",
+ "pallet-balances",
+ "pallet-timestamp",
+ "parity-scale-codec",
+ "scale-info",
+ "serde",
+ "sp-arithmetic",
+ "sp-core",
+ "sp-runtime",
+ "sp-std",
+]
+
[[package]]
name = "pallet-bounties"
version = "4.0.0-dev"
@@ -7813,7 +7832,7 @@ dependencies = [
[[package]]
name = "pallet-ethereum"
version = "4.0.0-dev"
-source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#46cd85967849be241dac2bb72f561b942a463729"
+source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#a5481542518ec420352d263adcb2f78835ac9bc2"
dependencies = [
"ethereum",
"ethereum-types",
@@ -7860,7 +7879,7 @@ dependencies = [
[[package]]
name = "pallet-evm"
version = "6.0.0-dev"
-source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#46cd85967849be241dac2bb72f561b942a463729"
+source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#a5481542518ec420352d263adcb2f78835ac9bc2"
dependencies = [
"environmental",
"evm",
@@ -7885,7 +7904,7 @@ dependencies = [
[[package]]
name = "pallet-evm-chain-id"
version = "1.0.0-dev"
-source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#46cd85967849be241dac2bb72f561b942a463729"
+source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#a5481542518ec420352d263adcb2f78835ac9bc2"
dependencies = [
"frame-support",
"frame-system",
@@ -7922,7 +7941,7 @@ dependencies = [
[[package]]
name = "pallet-evm-precompile-blake2"
version = "2.0.0-dev"
-source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#46cd85967849be241dac2bb72f561b942a463729"
+source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#a5481542518ec420352d263adcb2f78835ac9bc2"
dependencies = [
"fp-evm",
]
@@ -7930,7 +7949,7 @@ dependencies = [
[[package]]
name = "pallet-evm-precompile-bn128"
version = "2.0.0-dev"
-source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#46cd85967849be241dac2bb72f561b942a463729"
+source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#a5481542518ec420352d263adcb2f78835ac9bc2"
dependencies = [
"fp-evm",
"sp-core",
@@ -7965,7 +7984,7 @@ dependencies = [
[[package]]
name = "pallet-evm-precompile-dispatch"
version = "2.0.0-dev"
-source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#46cd85967849be241dac2bb72f561b942a463729"
+source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#a5481542518ec420352d263adcb2f78835ac9bc2"
dependencies = [
"fp-evm",
"frame-support",
@@ -7975,7 +7994,7 @@ dependencies = [
[[package]]
name = "pallet-evm-precompile-ed25519"
version = "2.0.0-dev"
-source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#46cd85967849be241dac2bb72f561b942a463729"
+source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#a5481542518ec420352d263adcb2f78835ac9bc2"
dependencies = [
"ed25519-dalek",
"fp-evm",
@@ -7984,7 +8003,7 @@ dependencies = [
[[package]]
name = "pallet-evm-precompile-modexp"
version = "2.0.0-dev"
-source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#46cd85967849be241dac2bb72f561b942a463729"
+source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#a5481542518ec420352d263adcb2f78835ac9bc2"
dependencies = [
"fp-evm",
"num",
@@ -7993,7 +8012,7 @@ dependencies = [
[[package]]
name = "pallet-evm-precompile-sha3fips"
version = "2.0.0-dev"
-source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#46cd85967849be241dac2bb72f561b942a463729"
+source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#a5481542518ec420352d263adcb2f78835ac9bc2"
dependencies = [
"fp-evm",
"tiny-keccak",
@@ -8002,7 +8021,7 @@ dependencies = [
[[package]]
name = "pallet-evm-precompile-simple"
version = "2.0.0-dev"
-source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#46cd85967849be241dac2bb72f561b942a463729"
+source = "git+https://github.com/AstarNetwork/frontier?branch=polkadot-v0.9.43#a5481542518ec420352d263adcb2f78835ac9bc2"
dependencies = [
"fp-evm",
"ripemd",
@@ -13131,7 +13150,7 @@ dependencies = [
"pallet-aura",
"pallet-authorship",
"pallet-balances",
- "pallet-block-reward",
+ "pallet-block-rewards-hybrid",
"pallet-chain-extension-assets",
"pallet-chain-extension-dapps-staking",
"pallet-chain-extension-unified-accounts",
diff --git a/Cargo.toml b/Cargo.toml
index 70c72ef58f..e451b03095 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -273,6 +273,7 @@ orml-xcm-support = { git = "https://github.com/open-web3-stack/open-runtime-modu
# Astar pallets & modules
# (wasm)
pallet-block-reward = { path = "./pallets/block-reward", default-features = false }
+pallet-block-rewards-hybrid = { path = "./pallets/block-rewards-hybrid", default-features = false }
pallet-collator-selection = { path = "./pallets/collator-selection", default-features = false }
pallet-dapps-staking = { path = "./pallets/dapps-staking", default-features = false }
pallet-xc-asset-config = { path = "./pallets/xc-asset-config", default-features = false }
diff --git a/bin/collator/src/local/chain_spec.rs b/bin/collator/src/local/chain_spec.rs
index 798370c075..d18ee5c097 100644
--- a/bin/collator/src/local/chain_spec.rs
+++ b/bin/collator/src/local/chain_spec.rs
@@ -21,8 +21,8 @@
use local_runtime::{
wasm_binary_unwrap, AccountId, AuraConfig, AuraId, BalancesConfig, BlockRewardConfig,
CouncilConfig, DemocracyConfig, EVMConfig, GenesisConfig, GrandpaConfig, GrandpaId,
- Precompiles, Signature, SudoConfig, SystemConfig, TechnicalCommitteeConfig, TreasuryConfig,
- VestingConfig,
+ Precompiles, RewardDistributionConfig, Signature, SudoConfig, SystemConfig,
+ TechnicalCommitteeConfig, TreasuryConfig, VestingConfig,
};
use sc_service::ChainType;
use sp_core::{crypto::Ss58Codec, sr25519, Pair, Public};
@@ -117,8 +117,8 @@ fn testnet_genesis(
},
block_reward: BlockRewardConfig {
// Make sure sum is 100
- reward_config: pallet_block_reward::RewardDistributionConfig {
- base_treasury_percent: Perbill::from_percent(25),
+ reward_config: RewardDistributionConfig {
+ treasury_percent: Perbill::from_percent(25),
base_staker_percent: Perbill::from_percent(30),
dapps_percent: Perbill::from_percent(20),
collators_percent: Perbill::zero(),
diff --git a/bin/collator/src/parachain/chain_spec/shibuya.rs b/bin/collator/src/parachain/chain_spec/shibuya.rs
index bdd00127c0..ac4478afe1 100644
--- a/bin/collator/src/parachain/chain_spec/shibuya.rs
+++ b/bin/collator/src/parachain/chain_spec/shibuya.rs
@@ -23,8 +23,9 @@ use sc_service::ChainType;
use shibuya_runtime::{
wasm_binary_unwrap, AccountId, AuraConfig, AuraId, Balance, BalancesConfig, BlockRewardConfig,
CollatorSelectionConfig, CouncilConfig, DemocracyConfig, EVMChainIdConfig, EVMConfig,
- GenesisConfig, ParachainInfoConfig, Precompiles, SessionConfig, SessionKeys, Signature,
- SudoConfig, SystemConfig, TechnicalCommitteeConfig, TreasuryConfig, VestingConfig, SBY,
+ GenesisConfig, ParachainInfoConfig, Precompiles, RewardDistributionConfig, SessionConfig,
+ SessionKeys, Signature, SudoConfig, SystemConfig, TechnicalCommitteeConfig, TreasuryConfig,
+ VestingConfig, SBY,
};
use sp_core::{sr25519, Pair, Public};
@@ -115,8 +116,8 @@ fn make_genesis(
balances: BalancesConfig { balances },
block_reward: BlockRewardConfig {
// Make sure sum is 100
- reward_config: pallet_block_reward::RewardDistributionConfig {
- base_treasury_percent: Perbill::from_percent(10),
+ reward_config: RewardDistributionConfig {
+ treasury_percent: Perbill::from_percent(10),
base_staker_percent: Perbill::from_percent(20),
dapps_percent: Perbill::from_percent(20),
collators_percent: Perbill::from_percent(5),
diff --git a/pallets/block-rewards-hybrid/Cargo.toml b/pallets/block-rewards-hybrid/Cargo.toml
new file mode 100644
index 0000000000..b5b6486d18
--- /dev/null
+++ b/pallets/block-rewards-hybrid/Cargo.toml
@@ -0,0 +1,51 @@
+[package]
+name = "pallet-block-rewards-hybrid"
+version = "0.1.0"
+license = "Apache-2.0"
+description = "FRAME pallet for managing block reward issuance & distribution"
+authors.workspace = true
+edition.workspace = true
+homepage.workspace = true
+repository.workspace = true
+
+[dependencies]
+parity-scale-codec = { workspace = true }
+serde = { workspace = true }
+
+astar-primitives = { workspace = true }
+frame-support = { workspace = true }
+frame-system = { workspace = true }
+scale-info = { workspace = true }
+sp-arithmetic = { workspace = true }
+sp-runtime = { workspace = true }
+sp-std = { workspace = true }
+
+frame-benchmarking = { workspace = true, optional = true }
+
+[dev-dependencies]
+pallet-balances = { workspace = true }
+pallet-timestamp = { workspace = true }
+sp-core = { workspace = true }
+
+[features]
+default = ["std"]
+std = [
+ "parity-scale-codec/std",
+ "sp-core/std",
+ "scale-info/std",
+ "sp-std/std",
+ "serde/std",
+ "frame-support/std",
+ "frame-system/std",
+ "pallet-timestamp/std",
+ "pallet-balances/std",
+ "astar-primitives/std",
+]
+runtime-benchmarks = [
+ "frame-benchmarking",
+ "frame-support/runtime-benchmarks",
+ "frame-system/runtime-benchmarks",
+ "sp-runtime/runtime-benchmarks",
+ "astar-primitives/runtime-benchmarks",
+]
+try-runtime = ["frame-support/try-runtime"]
diff --git a/pallets/block-rewards-hybrid/src/benchmarking.rs b/pallets/block-rewards-hybrid/src/benchmarking.rs
new file mode 100644
index 0000000000..d24f80f049
--- /dev/null
+++ b/pallets/block-rewards-hybrid/src/benchmarking.rs
@@ -0,0 +1,61 @@
+// This file is part of Astar.
+
+// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+// Astar 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.
+
+// Astar 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 Astar. If not, see .
+
+#![cfg(feature = "runtime-benchmarks")]
+
+use super::*;
+
+use frame_benchmarking::v2::*;
+use frame_system::{Pallet as System, RawOrigin};
+
+/// Assert that the last event equals the provided one.
+fn assert_last_event(generic_event: ::RuntimeEvent) {
+ System::::assert_last_event(generic_event.into());
+}
+
+#[benchmarks(where T: Config)]
+mod benchmarks {
+ use super::*;
+
+ #[benchmark]
+ fn set_configuration() {
+ let reward_config = RewardDistributionConfig::default();
+ assert!(reward_config.is_consistent());
+
+ #[extrinsic_call]
+ _(RawOrigin::Root, reward_config.clone());
+
+ assert_last_event::(Event::::DistributionConfigurationChanged(reward_config).into());
+ }
+
+ impl_benchmark_test_suite!(
+ Pallet,
+ crate::benchmarking::tests::new_test_ext(),
+ crate::mock::TestRuntime,
+ );
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::mock;
+ use frame_support::sp_io::TestExternalities;
+
+ pub fn new_test_ext() -> TestExternalities {
+ mock::ExternalityBuilder::build()
+ }
+}
diff --git a/pallets/block-rewards-hybrid/src/lib.rs b/pallets/block-rewards-hybrid/src/lib.rs
new file mode 100644
index 0000000000..7b6d3c7c81
--- /dev/null
+++ b/pallets/block-rewards-hybrid/src/lib.rs
@@ -0,0 +1,385 @@
+// This file is part of Astar.
+
+// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+// Astar 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.
+
+// Astar 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 Astar. If not, see .
+
+//! # Block Reward Distribution Pallet
+//!
+//! - [`Config`]
+//!
+//! ## Overview
+//!
+//! Pallet that implements block reward issuance and distribution mechanics.
+//!
+//! After issuing a block reward, pallet will calculate how to distribute the reward
+//! based on configurable parameters and chain state.
+//!
+//! Major on-chain factors which can influence reward distribution are total issuance and total value locked by dapps staking.
+//!
+//! ## Interface
+//!
+//! ### Dispatchable Function
+//!
+//! - `set_configuration` - used to change reward distribution configuration parameters
+//!
+//! ### Other
+//!
+//! - `on_timestamp_set` - This pallet implements the `OnTimestampSet` trait to handle block production.
+//! Note: We assume that it's impossible to set timestamp two times in a block.
+//!
+//! ## Usage
+//!
+//! 1. Pallet should be set as a handler of `OnTimestampSet`.
+//! 2. `DappsStakingTvlProvider` handler should be defined as an impl of `TvlProvider` trait. For example:
+//! ```nocompile
+//! pub struct TvlProvider();
+//! impl Get for TvlProvider {
+//! fn tvl() -> Balance {
+//! DappsStaking::total_locked_value()
+//! }
+//! }
+//! ```
+//! 3. `BeneficiaryPayout` handler should be defined as an impl of `BeneficiaryPayout` trait. For example:
+//! ```nocompile
+//! pub struct BeneficiaryPayout();
+//! impl BeneficiaryPayout> for BeneficiaryPayout {
+//!
+//! fn treasury(reward: NegativeImbalanceOf) {
+//! Balances::resolve_creating(&TREASURY_POT.into_account(), reward);
+//! }
+//!
+//! fn collators(reward: NegativeImbalanceOf) {
+//! Balances::resolve_creating(&COLLATOR_POT.into_account(), reward);
+//! }
+//!
+//! fn dapps_staking(stakers: NegativeImbalanceOf, dapps: NegativeImbalanceOf) {
+//! DappsStaking::rewards(stakers, dapps);
+//! }
+//! }
+//! ```
+//! 4. Set `MaxBlockRewardAmount` to the max reward amount distributed per block.
+//! Max amount will be reached if `ideal_dapps_staking_tvl` is reached.
+//!
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+pub use pallet::*;
+
+use astar_primitives::Balance;
+use frame_support::pallet_prelude::*;
+use frame_support::{
+ log,
+ traits::{Currency, Get, Imbalance, OnTimestampSet},
+};
+use frame_system::{ensure_root, pallet_prelude::*};
+use sp_runtime::{
+ traits::{CheckedAdd, Zero},
+ Perbill,
+};
+use sp_std::vec;
+
+#[cfg(any(feature = "runtime-benchmarks"))]
+pub mod benchmarking;
+#[cfg(test)]
+mod mock;
+#[cfg(test)]
+mod tests;
+
+pub mod weights;
+pub use weights::WeightInfo;
+
+#[frame_support::pallet]
+pub mod pallet {
+
+ use super::*;
+
+ #[pallet::pallet]
+ pub struct Pallet(PhantomData);
+
+ // Negative imbalance type of this pallet.
+ pub(crate) type NegativeImbalanceOf = <::Currency as Currency<
+ ::AccountId,
+ >>::NegativeImbalance;
+
+ #[pallet::config]
+ pub trait Config: frame_system::Config {
+ /// The currency trait.
+ type Currency: Currency;
+
+ /// Provides information about how much value is locked by dapps staking
+ type DappsStakingTvlProvider: Get;
+
+ /// Used to payout rewards
+ type BeneficiaryPayout: BeneficiaryPayout>;
+
+ /// The amount of issuance for each block.
+ #[pallet::constant]
+ type MaxBlockRewardAmount: Get;
+
+ /// The overarching event type.
+ type RuntimeEvent: From> + IsType<::RuntimeEvent>;
+
+ /// Weight information for extrinsics in this pallet.
+ type WeightInfo: WeightInfo;
+ }
+
+ #[pallet::storage]
+ #[pallet::getter(fn reward_config)]
+ pub type RewardDistributionConfigStorage =
+ StorageValue<_, RewardDistributionConfig, ValueQuery>;
+
+ #[pallet::event]
+ #[pallet::generate_deposit(pub(crate) fn deposit_event)]
+ pub enum Event {
+ /// Distribution configuration has been updated.
+ DistributionConfigurationChanged(RewardDistributionConfig),
+ }
+
+ #[pallet::error]
+ pub enum Error {
+ /// Sum of all rations must be one whole (100%)
+ InvalidDistributionConfiguration,
+ }
+
+ #[pallet::genesis_config]
+ #[cfg_attr(feature = "std", derive(Default))]
+ pub struct GenesisConfig {
+ pub reward_config: RewardDistributionConfig,
+ }
+
+ #[pallet::genesis_build]
+ impl GenesisBuild for GenesisConfig {
+ fn build(&self) {
+ assert!(self.reward_config.is_consistent());
+ RewardDistributionConfigStorage::::put(self.reward_config.clone())
+ }
+ }
+
+ #[pallet::call]
+ impl Pallet {
+ /// Sets the reward distribution configuration parameters which will be used from next block reward distribution.
+ ///
+ /// It is mandatory that all components of configuration sum up to one whole (**100%**),
+ /// otherwise an error `InvalidDistributionConfiguration` will be raised.
+ ///
+ /// - `reward_distro_params` - reward distribution params
+ ///
+ /// Emits `DistributionConfigurationChanged` with config embeded into event itself.
+ ///
+ #[pallet::call_index(0)]
+ #[pallet::weight(T::WeightInfo::set_configuration())]
+ pub fn set_configuration(
+ origin: OriginFor,
+ reward_distro_params: RewardDistributionConfig,
+ ) -> DispatchResultWithPostInfo {
+ ensure_root(origin)?;
+
+ ensure!(
+ reward_distro_params.is_consistent(),
+ Error::::InvalidDistributionConfiguration
+ );
+ RewardDistributionConfigStorage::::put(reward_distro_params.clone());
+
+ Self::deposit_event(Event::::DistributionConfigurationChanged(
+ reward_distro_params,
+ ));
+
+ Ok(().into())
+ }
+ }
+
+ impl OnTimestampSet for Pallet {
+ fn on_timestamp_set(_moment: Moment) {
+ let rewards = Self::calculate_rewards(T::MaxBlockRewardAmount::get());
+ let inflation = T::Currency::issue(rewards.sum());
+ Self::distribute_rewards(inflation, rewards);
+ }
+ }
+
+ impl Pallet {
+ /// Calculates the amount of rewards for each beneficiary
+ ///
+ /// # Arguments
+ /// * `block_reward` - the block reward amount
+ ///
+ fn calculate_rewards(block_reward: Balance) -> Rewards {
+ let distro_params = Self::reward_config();
+
+ // Pre-calculate balance which will be deposited for each beneficiary
+ let base_staker_balance = distro_params.base_staker_percent * block_reward;
+ let dapps_reward = distro_params.dapps_percent * block_reward;
+ let collators_reward = distro_params.collators_percent * block_reward;
+ let treasury_reward = distro_params.treasury_percent * block_reward;
+
+ // This is part is the TVL dependant staker reward
+ let adjustable_balance = distro_params.adjustable_percent * block_reward;
+
+ // Calculate total staker reward
+ let adjustable_staker_part = if distro_params.ideal_dapps_staking_tvl.is_zero() {
+ adjustable_balance
+ } else {
+ Self::tvl_percentage() / distro_params.ideal_dapps_staking_tvl * adjustable_balance
+ };
+
+ let staker_reward = base_staker_balance.saturating_add(adjustable_staker_part);
+
+ Rewards {
+ treasury_reward,
+ staker_reward,
+ dapps_reward,
+ collators_reward,
+ }
+ }
+
+ /// Distribute reward between beneficiaries.
+ ///
+ /// # Arguments
+ /// * `inflation` - inflation issued for this block
+ /// * `rewards` - rewards that will be split and distributed
+ ///
+ fn distribute_rewards(inflation: NegativeImbalanceOf, rewards: Rewards) {
+ // Prepare imbalances
+ let (dapps_imbalance, remainder) = inflation.split(rewards.dapps_reward);
+ let (stakers_imbalance, remainder) = remainder.split(rewards.staker_reward);
+ let (collator_imbalance, remainder) = remainder.split(rewards.collators_reward);
+ let (treasury_imbalance, _) = remainder.split(rewards.treasury_reward);
+
+ // Payout beneficiaries
+ T::BeneficiaryPayout::treasury(treasury_imbalance);
+ T::BeneficiaryPayout::collators(collator_imbalance);
+ T::BeneficiaryPayout::dapps_staking(stakers_imbalance, dapps_imbalance);
+ }
+
+ /// Provides TVL as percentage of total issuance
+ fn tvl_percentage() -> Perbill {
+ let total_issuance = T::Currency::total_issuance();
+ if total_issuance.is_zero() {
+ log::warn!("Total issuance is zero - this should be impossible.");
+ Zero::zero()
+ } else {
+ Perbill::from_rational(T::DappsStakingTvlProvider::get(), total_issuance)
+ }
+ }
+ }
+}
+
+/// List of configuration parameters used to calculate reward distribution portions for all the beneficiaries.
+///
+/// Note that if `ideal_dapps_staking_tvl` is set to `Zero`, entire `adjustable_percent` goes to the stakers.
+///
+#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]
+#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
+pub struct RewardDistributionConfig {
+ /// Base percentage of reward that goes to treasury
+ #[codec(compact)]
+ pub treasury_percent: Perbill,
+ /// Base percentage of reward that goes to stakers
+ #[codec(compact)]
+ pub base_staker_percent: Perbill,
+ /// Percentage of rewards that goes to dApps
+ #[codec(compact)]
+ pub dapps_percent: Perbill,
+ /// Percentage of reward that goes to collators
+ #[codec(compact)]
+ pub collators_percent: Perbill,
+ /// Adjustable reward percentage that either goes to treasury or to stakers
+ #[codec(compact)]
+ pub adjustable_percent: Perbill,
+ /// Target dapps-staking TVL percentage at which adjustable inflation towards stakers becomes saturated
+ #[codec(compact)]
+ pub ideal_dapps_staking_tvl: Perbill,
+}
+
+impl Default for RewardDistributionConfig {
+ /// `default` values based on configuration at the time of writing this code.
+ /// Should be overriden by desired params.
+ fn default() -> Self {
+ RewardDistributionConfig {
+ treasury_percent: Perbill::from_percent(40),
+ base_staker_percent: Perbill::from_percent(25),
+ dapps_percent: Perbill::from_percent(25),
+ collators_percent: Perbill::from_percent(10),
+ adjustable_percent: Zero::zero(),
+ ideal_dapps_staking_tvl: Zero::zero(),
+ }
+ }
+}
+
+impl RewardDistributionConfig {
+ /// `true` if sum of all percentages is `one whole`, `false` otherwise.
+ pub fn is_consistent(&self) -> bool {
+ // TODO: perhaps this can be writen in a more cleaner way?
+ // experimental-only `try_reduce` could be used but it's not available
+ // https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.try_reduce
+
+ let variables = vec![
+ &self.treasury_percent,
+ &self.base_staker_percent,
+ &self.dapps_percent,
+ &self.collators_percent,
+ &self.adjustable_percent,
+ ];
+
+ let mut accumulator = Perbill::zero();
+ for config_param in variables {
+ let result = accumulator.checked_add(config_param);
+ if let Some(mid_result) = result {
+ accumulator = mid_result;
+ } else {
+ return false;
+ }
+ }
+
+ Perbill::one() == accumulator
+ }
+}
+
+/// Represents rewards distribution balances for each beneficiary
+#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)]
+#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
+pub struct Rewards {
+ treasury_reward: Balance,
+ staker_reward: Balance,
+ dapps_reward: Balance,
+ collators_reward: Balance,
+}
+
+impl Rewards {
+ fn sum(&self) -> Balance {
+ self.treasury_reward
+ .saturating_add(self.staker_reward)
+ .saturating_add(self.dapps_reward)
+ .saturating_add(self.collators_reward)
+ }
+}
+
+/// Defines functions used to payout the beneficiaries of block rewards
+pub trait BeneficiaryPayout {
+ /// Payout reward to the treasury
+ fn treasury(reward: Imbalance);
+
+ /// Payout reward to the collators
+ fn collators(reward: Imbalance);
+
+ /// Payout reward to dapps staking
+ ///
+ /// # Arguments
+ ///
+ /// * `stakers` - reward that goes towards staker reward pot
+ /// * `dapps` - reward that goes towards dapps reward pot
+ ///
+ fn dapps_staking(stakers: Imbalance, dapps: Imbalance);
+}
diff --git a/pallets/block-rewards-hybrid/src/mock.rs b/pallets/block-rewards-hybrid/src/mock.rs
new file mode 100644
index 0000000000..80e10e6eeb
--- /dev/null
+++ b/pallets/block-rewards-hybrid/src/mock.rs
@@ -0,0 +1,206 @@
+// This file is part of Astar.
+
+// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+// Astar 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.
+
+// Astar 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 Astar. If not, see .
+
+use crate::{self as pallet_block_reward, NegativeImbalanceOf};
+
+use frame_support::{
+ construct_runtime, parameter_types,
+ sp_io::TestExternalities,
+ traits::Currency,
+ traits::{ConstU32, Get},
+ weights::Weight,
+ PalletId,
+};
+
+use sp_core::H256;
+use sp_runtime::{
+ testing::Header,
+ traits::{AccountIdConversion, BlakeTwo256, IdentityLookup},
+};
+use sp_std::cell::RefCell;
+
+pub(crate) type AccountId = u64;
+pub(crate) type BlockNumber = u64;
+pub(crate) type Balance = u128;
+
+type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic;
+type Block = frame_system::mocking::MockBlock;
+
+/// Value shouldn't be less than 2 for testing purposes, otherwise we cannot test certain corner cases.
+pub(crate) const EXISTENTIAL_DEPOSIT: Balance = 2;
+
+construct_runtime!(
+ pub struct TestRuntime
+ where
+ Block = Block,
+ NodeBlock = Block,
+ UncheckedExtrinsic = UncheckedExtrinsic,
+ {
+ System: frame_system,
+ Balances: pallet_balances,
+ Timestamp: pallet_timestamp,
+ BlockReward: pallet_block_reward,
+ }
+);
+
+parameter_types! {
+ pub const BlockHashCount: u64 = 250;
+ pub BlockWeights: frame_system::limits::BlockWeights =
+ frame_system::limits::BlockWeights::simple_max(Weight::from_parts(1024, 0));
+}
+
+impl frame_system::Config for TestRuntime {
+ type BaseCallFilter = frame_support::traits::Everything;
+ type BlockWeights = ();
+ type BlockLength = ();
+ type RuntimeOrigin = RuntimeOrigin;
+ type Index = u64;
+ type RuntimeCall = RuntimeCall;
+ type BlockNumber = BlockNumber;
+ type Hash = H256;
+ type Hashing = BlakeTwo256;
+ type AccountId = AccountId;
+ type Lookup = IdentityLookup;
+ type Header = Header;
+ type RuntimeEvent = RuntimeEvent;
+ type BlockHashCount = BlockHashCount;
+ type DbWeight = ();
+ type Version = ();
+ type PalletInfo = PalletInfo;
+ type AccountData = pallet_balances::AccountData;
+ type OnNewAccount = ();
+ type OnKilledAccount = ();
+ type SystemWeightInfo = ();
+ type SS58Prefix = ();
+ type OnSetCode = ();
+ type MaxConsumers = frame_support::traits::ConstU32<16>;
+}
+
+parameter_types! {
+ pub const MaxLocks: u32 = 4;
+ pub const ExistentialDeposit: Balance = EXISTENTIAL_DEPOSIT;
+}
+
+impl pallet_balances::Config for TestRuntime {
+ type MaxLocks = MaxLocks;
+ type MaxReserves = ();
+ type ReserveIdentifier = [u8; 8];
+ type Balance = Balance;
+ type RuntimeEvent = RuntimeEvent;
+ type DustRemoval = ();
+ type ExistentialDeposit = ExistentialDeposit;
+ type AccountStore = System;
+ type WeightInfo = ();
+ type HoldIdentifier = ();
+ type FreezeIdentifier = ();
+ type MaxHolds = ConstU32<0>;
+ type MaxFreezes = ConstU32<0>;
+}
+
+parameter_types! {
+ pub const MinimumPeriod: u64 = 3;
+}
+
+impl pallet_timestamp::Config for TestRuntime {
+ type Moment = u64;
+ type OnTimestampSet = ();
+ type MinimumPeriod = MinimumPeriod;
+ type WeightInfo = ();
+}
+
+// A fairly high block reward so we can detect slight changes in reward distribution
+// due to TVL changes.
+pub(crate) const BLOCK_REWARD: Balance = 1_000_000;
+
+// Fake accounts used to simulate reward beneficiaries balances
+pub(crate) const TREASURY_POT: PalletId = PalletId(*b"moktrsry");
+pub(crate) const COLLATOR_POT: PalletId = PalletId(*b"mokcolat");
+pub(crate) const STAKERS_POT: PalletId = PalletId(*b"mokstakr");
+pub(crate) const DAPPS_POT: PalletId = PalletId(*b"mokdapps");
+
+thread_local! {
+ static TVL: RefCell = RefCell::new(1_000_000_000);
+}
+
+// Type used as TVL provider
+pub struct TvlProvider();
+impl Get for TvlProvider {
+ fn get() -> Balance {
+ TVL.with(|t| t.borrow().clone())
+ }
+}
+
+pub(crate) fn set_tvl(v: Balance) {
+ TVL.with(|t| *t.borrow_mut() = v)
+}
+
+// Type used as beneficiary payout handle
+pub struct BeneficiaryPayout();
+impl pallet_block_reward::BeneficiaryPayout>
+ for BeneficiaryPayout
+{
+ fn treasury(reward: NegativeImbalanceOf) {
+ Balances::resolve_creating(&TREASURY_POT.into_account_truncating(), reward);
+ }
+
+ fn collators(reward: NegativeImbalanceOf) {
+ Balances::resolve_creating(&COLLATOR_POT.into_account_truncating(), reward);
+ }
+
+ fn dapps_staking(
+ stakers: NegativeImbalanceOf,
+ dapps: NegativeImbalanceOf,
+ ) {
+ Balances::resolve_creating(&STAKERS_POT.into_account_truncating(), stakers);
+ Balances::resolve_creating(&DAPPS_POT.into_account_truncating(), dapps);
+ }
+}
+
+parameter_types! {
+ pub const RewardAmount: Balance = BLOCK_REWARD;
+}
+
+impl pallet_block_reward::Config for TestRuntime {
+ type RuntimeEvent = RuntimeEvent;
+ type Currency = Balances;
+ type MaxBlockRewardAmount = RewardAmount;
+ type DappsStakingTvlProvider = TvlProvider;
+ type BeneficiaryPayout = BeneficiaryPayout;
+ type WeightInfo = ();
+}
+
+pub struct ExternalityBuilder;
+
+impl ExternalityBuilder {
+ pub fn build() -> TestExternalities {
+ let mut storage = frame_system::GenesisConfig::default()
+ .build_storage::()
+ .unwrap();
+
+ // This will cause some initial issuance
+ pallet_balances::GenesisConfig:: {
+ balances: vec![(1, 9000), (2, 800), (3, 10000)],
+ }
+ .assimilate_storage(&mut storage)
+ .ok();
+
+ let mut ext = TestExternalities::from(storage);
+ ext.execute_with(|| System::set_block_number(1));
+ ext
+ }
+}
diff --git a/pallets/block-rewards-hybrid/src/tests.rs b/pallets/block-rewards-hybrid/src/tests.rs
new file mode 100644
index 0000000000..0ba92d500a
--- /dev/null
+++ b/pallets/block-rewards-hybrid/src/tests.rs
@@ -0,0 +1,445 @@
+// This file is part of Astar.
+
+// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+// Astar 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.
+
+// Astar 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 Astar. If not, see .
+
+use super::{pallet::Error, Event, *};
+use frame_support::{assert_noop, assert_ok, traits::OnTimestampSet};
+use mock::{Balance, *};
+use sp_runtime::{
+ traits::{AccountIdConversion, BadOrigin, Zero},
+ Perbill,
+};
+
+#[test]
+fn default_reward_distribution_config_is_consitent() {
+ let reward_config = RewardDistributionConfig::default();
+ assert!(reward_config.is_consistent());
+}
+
+#[test]
+fn reward_distribution_config_is_consistent() {
+ // 1
+ let reward_config = RewardDistributionConfig {
+ treasury_percent: Perbill::from_percent(100),
+ base_staker_percent: Zero::zero(),
+ dapps_percent: Zero::zero(),
+ collators_percent: Zero::zero(),
+ adjustable_percent: Zero::zero(),
+ ideal_dapps_staking_tvl: Zero::zero(),
+ };
+ assert!(reward_config.is_consistent());
+
+ // 2
+ let reward_config = RewardDistributionConfig {
+ treasury_percent: Zero::zero(),
+ base_staker_percent: Perbill::from_percent(100),
+ dapps_percent: Zero::zero(),
+ collators_percent: Zero::zero(),
+ adjustable_percent: Zero::zero(),
+ ideal_dapps_staking_tvl: Zero::zero(),
+ };
+ assert!(reward_config.is_consistent());
+
+ // 3
+ let reward_config = RewardDistributionConfig {
+ treasury_percent: Zero::zero(),
+ base_staker_percent: Zero::zero(),
+ dapps_percent: Zero::zero(),
+ collators_percent: Zero::zero(),
+ adjustable_percent: Perbill::from_percent(100),
+ ideal_dapps_staking_tvl: Perbill::from_percent(13),
+ };
+ assert!(reward_config.is_consistent());
+
+ // 4
+ // 100%
+ let reward_config = RewardDistributionConfig {
+ treasury_percent: Perbill::from_rational(4663701u32, 100000000u32),
+ base_staker_percent: Perbill::from_rational(2309024u32, 10000000u32),
+ dapps_percent: Perbill::from_rational(173094531u32, 1000000000u32),
+ collators_percent: Perbill::from_rational(29863296u32, 1000000000u32),
+ adjustable_percent: Perbill::from_rational(519502763u32, 1000000000u32),
+ ideal_dapps_staking_tvl: Perbill::from_percent(60),
+ };
+ assert!(reward_config.is_consistent());
+}
+
+#[test]
+fn reward_distribution_config_not_consistent() {
+ // 1
+ let reward_config = RewardDistributionConfig {
+ treasury_percent: Perbill::from_percent(100),
+ ..Default::default()
+ };
+ assert!(!reward_config.is_consistent());
+
+ // 2
+ let reward_config = RewardDistributionConfig {
+ adjustable_percent: Perbill::from_percent(100),
+ ..Default::default()
+ };
+ assert!(!reward_config.is_consistent());
+
+ // 3
+ // 99%
+ let reward_config = RewardDistributionConfig {
+ treasury_percent: Perbill::from_percent(10),
+ base_staker_percent: Perbill::from_percent(20),
+ dapps_percent: Perbill::from_percent(20),
+ collators_percent: Perbill::from_percent(30),
+ adjustable_percent: Perbill::from_percent(19),
+ ideal_dapps_staking_tvl: Zero::zero(),
+ };
+ assert!(!reward_config.is_consistent());
+
+ // 4
+ // 101%
+ let reward_config = RewardDistributionConfig {
+ treasury_percent: Perbill::from_percent(10),
+ base_staker_percent: Perbill::from_percent(20),
+ dapps_percent: Perbill::from_percent(20),
+ collators_percent: Perbill::from_percent(31),
+ adjustable_percent: Perbill::from_percent(20),
+ ideal_dapps_staking_tvl: Zero::zero(),
+ };
+ assert!(!reward_config.is_consistent());
+}
+
+#[test]
+fn set_configuration_fails() {
+ ExternalityBuilder::build().execute_with(|| {
+ // 1
+ assert_noop!(
+ BlockReward::set_configuration(RuntimeOrigin::signed(1), Default::default()),
+ BadOrigin
+ );
+
+ // 2
+ let reward_config = RewardDistributionConfig {
+ treasury_percent: Perbill::from_percent(100),
+ ..Default::default()
+ };
+ assert!(!reward_config.is_consistent());
+ assert_noop!(
+ BlockReward::set_configuration(RuntimeOrigin::root(), reward_config),
+ Error::::InvalidDistributionConfiguration,
+ );
+ })
+}
+
+#[test]
+fn set_configuration_is_ok() {
+ ExternalityBuilder::build().execute_with(|| {
+ // custom config so it differs from the default one
+ let reward_config = RewardDistributionConfig {
+ treasury_percent: Perbill::from_percent(3),
+ base_staker_percent: Perbill::from_percent(14),
+ dapps_percent: Perbill::from_percent(18),
+ collators_percent: Perbill::from_percent(31),
+ adjustable_percent: Perbill::from_percent(34),
+ ideal_dapps_staking_tvl: Perbill::from_percent(87),
+ };
+ assert!(reward_config.is_consistent());
+
+ assert_ok!(BlockReward::set_configuration(
+ RuntimeOrigin::root(),
+ reward_config.clone()
+ ));
+ System::assert_last_event(mock::RuntimeEvent::BlockReward(
+ Event::DistributionConfigurationChanged(reward_config.clone()),
+ ));
+
+ assert_eq!(
+ RewardDistributionConfigStorage::::get(),
+ reward_config
+ );
+ })
+}
+
+#[test]
+fn inflation_and_total_issuance_as_expected() {
+ ExternalityBuilder::build().execute_with(|| {
+ let init_issuance = ::Currency::total_issuance();
+
+ for block in 0..10 {
+ assert_eq!(
+ ::Currency::total_issuance(),
+ block * BLOCK_REWARD + init_issuance
+ );
+ BlockReward::on_timestamp_set(0);
+ assert_eq!(
+ ::Currency::total_issuance(),
+ (block + 1) * BLOCK_REWARD + init_issuance
+ );
+ }
+ })
+}
+
+#[test]
+fn reward_distribution_as_expected() {
+ ExternalityBuilder::build().execute_with(|| {
+ // Ensure that initially, all beneficiaries have no free balance
+ let init_balance_snapshot = FreeBalanceSnapshot::new();
+ assert!(init_balance_snapshot.is_zero());
+
+ // Prepare a custom config (easily discernable percentages for visual verification)
+ let reward_config = RewardDistributionConfig {
+ treasury_percent: Perbill::from_percent(10),
+ base_staker_percent: Perbill::from_percent(20),
+ dapps_percent: Perbill::from_percent(25),
+ collators_percent: Perbill::from_percent(5),
+ adjustable_percent: Perbill::from_percent(40),
+ ideal_dapps_staking_tvl: Perbill::from_percent(50),
+ };
+ assert!(reward_config.is_consistent());
+ assert_ok!(BlockReward::set_configuration(
+ RuntimeOrigin::root(),
+ reward_config.clone()
+ ));
+
+ // Issue rewards a couple of times and verify distribution is as expected
+ // also ensure that the non distributed reward amount is burn
+ // (that the total issuance is only increased by the amount that has been rewarded)
+ for _block in 1..=100 {
+ // TVL amount is updated every block
+ // to ensure TVL ratio as expected
+ adjust_tvl(30);
+ let init_balance_state = FreeBalanceSnapshot::new();
+ let total_issuance_before = ::Currency::total_issuance();
+ let distributed_rewards = Rewards::calculate(&reward_config);
+
+ BlockReward::on_timestamp_set(0);
+
+ let final_balance_state = FreeBalanceSnapshot::new();
+ init_balance_state.assert_distribution(&final_balance_state, &distributed_rewards);
+
+ assert_eq!(
+ ::Currency::total_issuance(),
+ total_issuance_before + distributed_rewards.sum()
+ );
+ }
+ })
+}
+
+#[test]
+fn non_distributed_reward_amount_is_burned() {
+ ExternalityBuilder::build().execute_with(|| {
+ // Ensure that initially, all beneficiaries have no free balance
+ let init_balance_snapshot = FreeBalanceSnapshot::new();
+ assert!(init_balance_snapshot.is_zero());
+
+ // Prepare a custom config (easily discernible percentages for visual verification)
+ let reward_config = RewardDistributionConfig {
+ treasury_percent: Perbill::from_percent(10),
+ base_staker_percent: Perbill::from_percent(20),
+ dapps_percent: Perbill::from_percent(25),
+ collators_percent: Perbill::from_percent(5),
+ adjustable_percent: Perbill::from_percent(40),
+ ideal_dapps_staking_tvl: Perbill::from_percent(50),
+ };
+ assert!(reward_config.is_consistent());
+ assert_ok!(BlockReward::set_configuration(
+ RuntimeOrigin::root(),
+ reward_config.clone()
+ ));
+
+ for tvl in [30, 50, 70, 100] {
+ for _block in 1..=100 {
+ // TVL amount is updated every block
+ // to ensure TVL ratio as expected
+ adjust_tvl(tvl);
+ let total_issuance_before = ::Currency::total_issuance();
+ let distributed_rewards = Rewards::calculate(&reward_config);
+ let burned_amount = BLOCK_REWARD - distributed_rewards.sum();
+
+ BlockReward::on_timestamp_set(0);
+
+ assert_eq!(
+ ::Currency::total_issuance(),
+ total_issuance_before + BLOCK_REWARD - burned_amount
+ );
+ }
+ }
+ })
+}
+
+#[test]
+fn reward_distribution_no_adjustable_part() {
+ ExternalityBuilder::build().execute_with(|| {
+ let reward_config = RewardDistributionConfig {
+ treasury_percent: Perbill::from_percent(10),
+ base_staker_percent: Perbill::from_percent(45),
+ dapps_percent: Perbill::from_percent(40),
+ collators_percent: Perbill::from_percent(5),
+ adjustable_percent: Perbill::zero(),
+ ideal_dapps_staking_tvl: Perbill::from_percent(50), // this is irrelevant
+ };
+ assert!(reward_config.is_consistent());
+ assert_ok!(BlockReward::set_configuration(
+ RuntimeOrigin::root(),
+ reward_config.clone()
+ ));
+
+ // no adjustable part so we don't expect rewards to change with TVL percentage
+ let const_rewards = Rewards::calculate(&reward_config);
+
+ for _block in 1..=100 {
+ let init_balance_state = FreeBalanceSnapshot::new();
+ let rewards = Rewards::calculate(&reward_config);
+
+ assert_eq!(rewards, const_rewards);
+
+ BlockReward::on_timestamp_set(0);
+
+ let final_balance_state = FreeBalanceSnapshot::new();
+ init_balance_state.assert_distribution(&final_balance_state, &rewards);
+ }
+ })
+}
+
+#[test]
+fn reward_distribution_all_zero_except_one() {
+ ExternalityBuilder::build().execute_with(|| {
+ let reward_config = RewardDistributionConfig {
+ treasury_percent: Perbill::zero(),
+ base_staker_percent: Perbill::zero(),
+ dapps_percent: Perbill::zero(),
+ collators_percent: Perbill::zero(),
+ adjustable_percent: Perbill::one(),
+ ideal_dapps_staking_tvl: Perbill::from_percent(50), // this is irrelevant
+ };
+ assert!(reward_config.is_consistent());
+ assert_ok!(BlockReward::set_configuration(
+ RuntimeOrigin::root(),
+ reward_config.clone()
+ ));
+
+ for _block in 1..=10 {
+ let init_balance_state = FreeBalanceSnapshot::new();
+ let rewards = Rewards::calculate(&reward_config);
+
+ BlockReward::on_timestamp_set(0);
+
+ let final_balance_state = FreeBalanceSnapshot::new();
+ init_balance_state.assert_distribution(&final_balance_state, &rewards);
+ }
+ })
+}
+
+/// Represents free balance snapshot at a specific point in time
+#[derive(PartialEq, Eq, Clone, RuntimeDebug)]
+struct FreeBalanceSnapshot {
+ treasury: Balance,
+ collators: Balance,
+ stakers: Balance,
+ dapps: Balance,
+}
+
+impl FreeBalanceSnapshot {
+ /// Creates a new free balance snapshot using current balance state.
+ ///
+ /// Future balance changes won't be reflected in this instance.
+ fn new() -> Self {
+ Self {
+ treasury: ::Currency::free_balance(
+ &TREASURY_POT.into_account_truncating(),
+ ),
+ collators: ::Currency::free_balance(
+ &COLLATOR_POT.into_account_truncating(),
+ ),
+ stakers: ::Currency::free_balance(
+ &STAKERS_POT.into_account_truncating(),
+ ),
+ dapps: ::Currency::free_balance(
+ &DAPPS_POT.into_account_truncating(),
+ ),
+ }
+ }
+
+ /// `true` if all free balances equal `Zero`, `false` otherwise
+ fn is_zero(&self) -> bool {
+ self.treasury.is_zero()
+ && self.collators.is_zero()
+ && self.stakers.is_zero()
+ && self.dapps.is_zero()
+ }
+
+ /// Asserts that `post_reward_state` is as expected.
+ ///
+ /// Increase in balances, based on `rewards` values, is verified.
+ ///
+ fn assert_distribution(&self, post_reward_state: &Self, rewards: &Rewards) {
+ assert_eq!(
+ self.treasury + rewards.treasury_reward,
+ post_reward_state.treasury
+ );
+ assert_eq!(
+ self.stakers + rewards.staker_reward,
+ post_reward_state.stakers
+ );
+ assert_eq!(
+ self.collators + rewards.collators_reward,
+ post_reward_state.collators
+ );
+ assert_eq!(self.dapps + rewards.dapps_reward, post_reward_state.dapps);
+ }
+}
+
+impl Rewards {
+ /// Pre-calculates the reward distribution, using the provided `RewardDistributionConfig`.
+ /// Method assumes that total issuance will be increased by `BLOCK_REWARD`.
+ ///
+ /// Both current `total_issuance` and `TVL` are used. If these are changed after calling this function,
+ /// they won't be reflected in the struct.
+ ///
+ fn calculate(reward_config: &RewardDistributionConfig) -> Self {
+ // Calculate `tvl-independent` portions
+ let treasury_reward = reward_config.treasury_percent * BLOCK_REWARD;
+ let base_staker_reward = reward_config.base_staker_percent * BLOCK_REWARD;
+ let dapps_reward = reward_config.dapps_percent * BLOCK_REWARD;
+ let collators_reward = reward_config.collators_percent * BLOCK_REWARD;
+ let adjustable_reward = reward_config.adjustable_percent * BLOCK_REWARD;
+
+ // Calculate `tvl-dependent` portions
+ let total_issuance = ::Currency::total_issuance();
+ let tvl = ::DappsStakingTvlProvider::get();
+ let tvl_percentage = Perbill::from_rational(tvl, total_issuance);
+
+ // Calculate factor for adjusting staker reward portion
+ let factor = if reward_config.ideal_dapps_staking_tvl.is_zero() {
+ Perbill::one()
+ } else {
+ tvl_percentage / reward_config.ideal_dapps_staking_tvl
+ };
+
+ // Adjustable reward portions
+ let adjustable_staker_reward = factor * adjustable_reward;
+
+ let staker_reward = base_staker_reward + adjustable_staker_reward;
+
+ Self {
+ treasury_reward,
+ staker_reward,
+ dapps_reward,
+ collators_reward,
+ }
+ }
+}
+
+fn adjust_tvl(desired_percent: u128) {
+ set_tvl(::Currency::total_issuance() / 100 * desired_percent);
+}
diff --git a/pallets/block-rewards-hybrid/src/weights.rs b/pallets/block-rewards-hybrid/src/weights.rs
new file mode 100644
index 0000000000..12c0be364f
--- /dev/null
+++ b/pallets/block-rewards-hybrid/src/weights.rs
@@ -0,0 +1,82 @@
+
+// This file is part of Astar.
+
+// Copyright (C) 2019-2023 Stake Technologies Pte.Ltd.
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+// Astar 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.
+
+// Astar 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 Astar. If not, see .
+
+//! Autogenerated weights for block_rewards_hybrid
+//!
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
+//! DATE: 2023-11-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! WORST CASE MAP SIZE: `1000000`
+//! HOSTNAME: `devserver-01`, CPU: `Intel(R) Xeon(R) E-2236 CPU @ 3.40GHz`
+//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("shibuya-dev"), DB CACHE: 1024
+
+// Executed Command:
+// ./target/release/astar-collator
+// benchmark
+// pallet
+// --chain=shibuya-dev
+// --steps=50
+// --repeat=20
+// --pallet=block_rewards_hybrid
+// --extrinsic=*
+// --execution=wasm
+// --wasm-execution=compiled
+// --heap-pages=4096
+// --output=./benchmark-results/shibuya-dev/rewards_hybrid_weights.rs
+// --template=./scripts/templates/weight-template.hbs
+
+#![cfg_attr(rustfmt, rustfmt_skip)]
+#![allow(unused_parens)]
+#![allow(unused_imports)]
+
+use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
+use core::marker::PhantomData;
+
+/// Weight functions needed for block_rewards_hybrid.
+pub trait WeightInfo {
+ fn set_configuration() -> Weight;
+}
+
+/// Weights for block_rewards_hybrid using the Substrate node and recommended hardware.
+pub struct SubstrateWeight(PhantomData);
+impl WeightInfo for SubstrateWeight {
+ /// Storage: BlockReward RewardDistributionConfigStorage (r:0 w:1)
+ /// Proof: BlockReward RewardDistributionConfigStorage (max_values: Some(1), max_size: Some(24), added: 519, mode: MaxEncodedLen)
+ fn set_configuration() -> Weight {
+ // Proof Size summary in bytes:
+ // Measured: `0`
+ // Estimated: `0`
+ // Minimum execution time: 8_645_000 picoseconds.
+ Weight::from_parts(8_848_000, 0)
+ .saturating_add(T::DbWeight::get().writes(1_u64))
+ }
+}
+
+// For backwards compatibility and tests
+impl WeightInfo for () {
+ /// Storage: BlockReward RewardDistributionConfigStorage (r:0 w:1)
+ /// Proof: BlockReward RewardDistributionConfigStorage (max_values: Some(1), max_size: Some(24), added: 519, mode: MaxEncodedLen)
+ fn set_configuration() -> Weight {
+ // Proof Size summary in bytes:
+ // Measured: `0`
+ // Estimated: `0`
+ // Minimum execution time: 8_645_000 picoseconds.
+ Weight::from_parts(8_848_000, 0)
+ .saturating_add(RocksDbWeight::get().writes(1_u64))
+ }
+}
\ No newline at end of file
diff --git a/runtime/local/Cargo.toml b/runtime/local/Cargo.toml
index 799532ed12..dfb5ab155a 100644
--- a/runtime/local/Cargo.toml
+++ b/runtime/local/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "local-runtime"
-version = "5.23.0"
+version = "5.24.0"
build = "build.rs"
authors.workspace = true
edition.workspace = true
@@ -65,7 +65,7 @@ pallet-transaction-payment-rpc-runtime-api = { workspace = true }
# Astar pallets
astar-primitives = { workspace = true }
-pallet-block-reward = { workspace = true }
+pallet-block-rewards-hybrid = { workspace = true }
pallet-chain-extension-dapps-staking = { workspace = true }
pallet-chain-extension-unified-accounts = { workspace = true }
pallet-chain-extension-xvm = { workspace = true }
@@ -111,7 +111,7 @@ std = [
"pallet-assets/std",
"pallet-aura/std",
"pallet-balances/std",
- "pallet-block-reward/std",
+ "pallet-block-rewards-hybrid/std",
"pallet-contracts/std",
"pallet-contracts-primitives/std",
"pallet-chain-extension-dapps-staking/std",
@@ -177,7 +177,7 @@ runtime-benchmarks = [
"frame-system/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
"pallet-dapps-staking/runtime-benchmarks",
- "pallet-block-reward/runtime-benchmarks",
+ "pallet-block-rewards-hybrid/runtime-benchmarks",
"pallet-balances/runtime-benchmarks",
"pallet-timestamp/runtime-benchmarks",
"pallet-ethereum/runtime-benchmarks",
@@ -198,7 +198,7 @@ try-runtime = [
"frame-system/try-runtime",
"pallet-aura/try-runtime",
"pallet-balances/try-runtime",
- "pallet-block-reward/try-runtime",
+ "pallet-block-rewards-hybrid/try-runtime",
"pallet-contracts/try-runtime",
"pallet-dapps-staking/try-runtime",
"pallet-grandpa/try-runtime",
diff --git a/runtime/local/src/lib.rs b/runtime/local/src/lib.rs
index 5b910c3ce8..f267be3e8f 100644
--- a/runtime/local/src/lib.rs
+++ b/runtime/local/src/lib.rs
@@ -65,6 +65,7 @@ pub use astar_primitives::{
evm::EvmRevertCodeHandler, AccountId, Address, AssetId, Balance, BlockNumber, Hash, Header,
Index, Signature,
};
+pub use pallet_block_rewards_hybrid::RewardDistributionConfig;
pub use crate::precompiles::WhitelistedCalls;
#[cfg(feature = "std")]
@@ -423,7 +424,7 @@ impl Get for DappsStakingTvlProvider {
}
pub struct BeneficiaryPayout();
-impl pallet_block_reward::BeneficiaryPayout for BeneficiaryPayout {
+impl pallet_block_rewards_hybrid::BeneficiaryPayout for BeneficiaryPayout {
fn treasury(reward: NegativeImbalance) {
Balances::resolve_creating(&TreasuryPalletId::get().into_account_truncating(), reward);
}
@@ -438,16 +439,16 @@ impl pallet_block_reward::BeneficiaryPayout for BeneficiaryPa
}
parameter_types! {
- pub const RewardAmount: Balance = 2_664 * MILLIAST;
+ pub const MaxBlockRewardAmount: Balance = 230_718 * MILLIAST;
}
-impl pallet_block_reward::Config for Runtime {
+impl pallet_block_rewards_hybrid::Config for Runtime {
type Currency = Balances;
type DappsStakingTvlProvider = DappsStakingTvlProvider;
type BeneficiaryPayout = BeneficiaryPayout;
- type RewardAmount = RewardAmount;
+ type MaxBlockRewardAmount = MaxBlockRewardAmount;
type RuntimeEvent = RuntimeEvent;
- type WeightInfo = pallet_block_reward::weights::SubstrateWeight;
+ type WeightInfo = pallet_block_rewards_hybrid::weights::SubstrateWeight;
}
parameter_types! {
@@ -1027,7 +1028,7 @@ construct_runtime!(
Balances: pallet_balances,
Vesting: pallet_vesting,
DappsStaking: pallet_dapps_staking,
- BlockReward: pallet_block_reward,
+ BlockReward: pallet_block_rewards_hybrid,
TransactionPayment: pallet_transaction_payment,
EVM: pallet_evm,
Ethereum: pallet_ethereum,
@@ -1157,7 +1158,7 @@ mod benches {
[pallet_balances, Balances]
[pallet_timestamp, Timestamp]
[pallet_dapps_staking, DappsStaking]
- [pallet_block_reward, BlockReward]
+ [pallet_block_rewards_hybrid, BlockReward]
[pallet_ethereum_checked, EthereumChecked]
[pallet_dynamic_evm_base_fee, DynamicEvmBaseFee]
);
diff --git a/runtime/shibuya/Cargo.toml b/runtime/shibuya/Cargo.toml
index 6267f2260c..2912090981 100644
--- a/runtime/shibuya/Cargo.toml
+++ b/runtime/shibuya/Cargo.toml
@@ -95,7 +95,7 @@ orml-xtokens = { workspace = true }
# Astar pallets
astar-primitives = { workspace = true }
-pallet-block-reward = { workspace = true }
+pallet-block-rewards-hybrid = { workspace = true }
pallet-chain-extension-dapps-staking = { workspace = true }
pallet-chain-extension-unified-accounts = { workspace = true }
pallet-chain-extension-xvm = { workspace = true }
@@ -158,7 +158,7 @@ std = [
"pallet-aura/std",
"pallet-assets/std",
"pallet-balances/std",
- "pallet-block-reward/std",
+ "pallet-block-rewards-hybrid/std",
"pallet-contracts/std",
"pallet-contracts-primitives/std",
"pallet-chain-extension-dapps-staking/std",
@@ -239,7 +239,7 @@ runtime-benchmarks = [
"frame-system/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
"pallet-dapps-staking/runtime-benchmarks",
- "pallet-block-reward/runtime-benchmarks",
+ "pallet-block-rewards-hybrid/runtime-benchmarks",
"pallet-balances/runtime-benchmarks",
"pallet-timestamp/runtime-benchmarks",
"pallet-collective/runtime-benchmarks",
@@ -266,7 +266,7 @@ try-runtime = [
"frame-system/try-runtime",
"pallet-aura/try-runtime",
"pallet-balances/try-runtime",
- "pallet-block-reward/try-runtime",
+ "pallet-block-rewards-hybrid/try-runtime",
"pallet-dapps-staking/try-runtime",
"pallet-sudo/try-runtime",
"pallet-timestamp/try-runtime",
diff --git a/runtime/shibuya/src/lib.rs b/runtime/shibuya/src/lib.rs
index fd7addf29d..3b9725bd23 100644
--- a/runtime/shibuya/src/lib.rs
+++ b/runtime/shibuya/src/lib.rs
@@ -72,6 +72,7 @@ pub use astar_primitives::{
xcm::AssetLocationIdConverter, AccountId, Address, AssetId, Balance, BlockNumber, Hash, Header,
Index, Signature,
};
+pub use pallet_block_rewards_hybrid::RewardDistributionConfig;
pub use crate::precompiles::WhitelistedCalls;
@@ -537,7 +538,7 @@ impl Get for DappsStakingTvlProvider {
}
pub struct BeneficiaryPayout();
-impl pallet_block_reward::BeneficiaryPayout for BeneficiaryPayout {
+impl pallet_block_rewards_hybrid::BeneficiaryPayout for BeneficiaryPayout {
fn treasury(reward: NegativeImbalance) {
Balances::resolve_creating(&TreasuryPalletId::get().into_account_truncating(), reward);
}
@@ -552,16 +553,16 @@ impl pallet_block_reward::BeneficiaryPayout for BeneficiaryPa
}
parameter_types! {
- pub const RewardAmount: Balance = 2_530 * MILLISBY;
+ pub const MaxBlockRewardAmount: Balance = 230_718 * MILLISBY;
}
-impl pallet_block_reward::Config for Runtime {
+impl pallet_block_rewards_hybrid::Config for Runtime {
type Currency = Balances;
type DappsStakingTvlProvider = DappsStakingTvlProvider;
type BeneficiaryPayout = BeneficiaryPayout;
- type RewardAmount = RewardAmount;
+ type MaxBlockRewardAmount = MaxBlockRewardAmount;
type RuntimeEvent = RuntimeEvent;
- type WeightInfo = pallet_block_reward::weights::SubstrateWeight;
+ type WeightInfo = pallet_block_rewards_hybrid::weights::SubstrateWeight;
}
parameter_types! {
@@ -1240,7 +1241,7 @@ construct_runtime!(
Balances: pallet_balances = 31,
Vesting: pallet_vesting = 32,
DappsStaking: pallet_dapps_staking = 34,
- BlockReward: pallet_block_reward = 35,
+ BlockReward: pallet_block_rewards_hybrid = 35,
Assets: pallet_assets = 36,
Authorship: pallet_authorship = 40,
@@ -1311,10 +1312,44 @@ pub type Executive = frame_executive::Executive<
Migrations,
>;
+pub use frame_support::traits::{OnRuntimeUpgrade, StorageVersion};
+pub struct HybridInflationModelMigration;
+impl OnRuntimeUpgrade for HybridInflationModelMigration {
+ fn on_runtime_upgrade() -> Weight {
+ let mut reward_config = pallet_block_rewards_hybrid::RewardDistributionConfig {
+ // 4.66%
+ treasury_percent: Perbill::from_rational(4_663_701u32, 100_000_000u32),
+ // 23.09%
+ base_staker_percent: Perbill::from_rational(2_309_024u32, 10_000_000u32),
+ // 17.31%
+ dapps_percent: Perbill::from_rational(173_094_531u32, 1_000_000_000u32),
+ // 2.99%
+ collators_percent: Perbill::from_rational(29_863_296u32, 1_000_000_000u32),
+ // 51.95%
+ adjustable_percent: Perbill::from_rational(519_502_763u32, 1_000_000_000u32),
+ // 60.00%
+ ideal_dapps_staking_tvl: Perbill::from_percent(60),
+ };
+
+ // This HAS to be tested prior to update - we need to ensure that config is consistent
+ #[cfg(feature = "try-runtime")]
+ assert!(reward_config.is_consistent());
+
+ // This should never execute but we need to have code in place that ensures config is consistent
+ if !reward_config.is_consistent() {
+ reward_config = Default::default();
+ }
+
+ pallet_block_rewards_hybrid::RewardDistributionConfigStorage::::put(reward_config);
+
+ ::DbWeight::get().writes(1)
+ }
+}
+
/// All migrations that will run on the next runtime upgrade.
///
/// Once done, migrations should be removed from the tuple.
-pub type Migrations = ();
+pub type Migrations = HybridInflationModelMigration;
type EventRecord = frame_system::EventRecord<
::RuntimeEvent,
@@ -1392,7 +1427,7 @@ mod benches {
[pallet_balances, Balances]
[pallet_timestamp, Timestamp]
[pallet_dapps_staking, DappsStaking]
- [pallet_block_reward, BlockReward]
+ [block_rewards_hybrid, BlockReward]
[pallet_xc_asset_config, XcAssetConfig]
[pallet_collator_selection, CollatorSelection]
[pallet_xcm, PolkadotXcm]