diff --git a/runtime/altair/src/lib.rs b/runtime/altair/src/lib.rs index 0092a6d796..eb9d32a613 100644 --- a/runtime/altair/src/lib.rs +++ b/runtime/altair/src/lib.rs @@ -103,6 +103,7 @@ use runtime_common::{ }, permissions::PoolAdminCheck, remarks::Remark, + rewards::SingleCurrencyMovement, transfer_filter::PreLpTransfer, xcm::AccountIdToMultiLocation, xcm_transactor, AllowanceDeposit, CurrencyED, HoldId, @@ -1204,9 +1205,6 @@ impl cumulus_pallet_dmp_queue::Config for Runtime { // Block Rewards parameter_types! { - // BlockRewards have exactly one group and currency - #[derive(scale_info::TypeInfo)] - pub const SingleCurrencyMovement: u32 = 1; #[derive(scale_info::TypeInfo, Debug, PartialEq, Eq, Clone)] pub const MaxChangesPerEpoch: u32 = 50; pub const BlockRewardsPalletId: PalletId = cfg_types::ids::BLOCK_REWARDS_PALLET_ID; diff --git a/runtime/centrifuge/src/lib.rs b/runtime/centrifuge/src/lib.rs index 36eba1f194..efd917c145 100644 --- a/runtime/centrifuge/src/lib.rs +++ b/runtime/centrifuge/src/lib.rs @@ -108,6 +108,7 @@ use runtime_common::{ }, origin::EnsureAccountOrRootOr, permissions::PoolAdminCheck, + rewards::SingleCurrencyMovement, transfer_filter::PreLpTransfer, xcm::AccountIdToMultiLocation, xcm_transactor, AllowanceDeposit, CurrencyED, HoldId, @@ -1220,9 +1221,6 @@ impl pallet_xcm_transactor::Config for Runtime { // Block Rewards parameter_types! { - // BlockRewards have exactly one group and currency - #[derive(scale_info::TypeInfo)] - pub const SingleCurrencyMovement: u32 = 1; #[derive(scale_info::TypeInfo, Debug, PartialEq, Eq, Clone)] pub const MaxChangesPerEpoch: u32 = 50; pub const BlockRewardsPalletId: PalletId = cfg_types::ids::BLOCK_REWARDS_PALLET_ID; diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 1bdb50ecf4..d91ada560b 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -694,3 +694,10 @@ pub mod permissions { } } } + +pub mod rewards { + frame_support::parameter_types! { + #[derive(scale_info::TypeInfo)] + pub const SingleCurrencyMovement: u32 = 1; + } +} diff --git a/runtime/development/src/lib.rs b/runtime/development/src/lib.rs index 97915ab363..b4de5c7766 100644 --- a/runtime/development/src/lib.rs +++ b/runtime/development/src/lib.rs @@ -112,6 +112,7 @@ use runtime_common::{ Feeder, OracleConverterBridge, OracleRatioProvider, OracleRatioProviderLocalAssetExtension, }, permissions::PoolAdminCheck, + rewards::SingleCurrencyMovement, transfer_filter::PreLpTransfer, xcm::AccountIdToMultiLocation, xcm_transactor, AllowanceDeposit, CurrencyED, HoldId, @@ -1692,7 +1693,7 @@ impl pallet_rewards::mechanism::gap::Config for Runtime { type Rate = FixedI128; } -impl pallet_rewards::Config for Runtime { +impl pallet_rewards::Config for Runtime { type Currency = Tokens; type CurrencyId = CurrencyId; type GroupId = u32; @@ -1719,13 +1720,7 @@ impl pallet_liquidity_rewards::Config for Runtime { type WeightInfo = (); } -parameter_types! { - // BlockRewards have exactly one group and currency - #[derive(scale_info::TypeInfo)] - pub const SingleCurrencyMovement: u32 = 1; -} - -impl pallet_rewards::Config for Runtime { +impl pallet_rewards::Config for Runtime { type Currency = Tokens; type CurrencyId = CurrencyId; type GroupId = u32; @@ -2105,11 +2100,11 @@ construct_runtime!( // Removed: Nft = 103 Keystore: pallet_keystore::{Pallet, Call, Storage, Event} = 104, Investments: pallet_investments::{Pallet, Call, Storage, Event} = 105, - LiquidityRewardsBase: pallet_rewards::::{Pallet, Storage, Event, Config} = 106, + LiquidityRewardsBase: pallet_rewards::::{Pallet, Storage, Event, Config} = 106, LiquidityRewards: pallet_liquidity_rewards::{Pallet, Call, Storage, Event} = 107, LiquidityPools: pallet_liquidity_pools::{Pallet, Call, Storage, Event} = 108, PoolRegistry: pallet_pool_registry::{Pallet, Call, Storage, Event} = 109, - BlockRewardsBase: pallet_rewards::::{Pallet, Storage, Event, Config} = 110, + BlockRewardsBase: pallet_rewards::::{Pallet, Storage, Event, Config} = 110, BlockRewards: pallet_block_rewards::{Pallet, Call, Storage, Event, Config} = 111, TransferAllowList: pallet_transfer_allowlist::{Pallet, Call, Storage, Event} = 112, GapRewardMechanism: pallet_rewards::mechanism::gap = 114, @@ -2440,15 +2435,15 @@ impl_runtime_apis! { impl runtime_common::apis::RewardsApi for Runtime { fn list_currencies(domain: runtime_common::apis::RewardDomain, account_id: AccountId) -> Vec { match domain { - runtime_common::apis::RewardDomain::Block => pallet_rewards::Pallet::::list_currencies(&account_id), - runtime_common::apis::RewardDomain::Liquidity => pallet_rewards::Pallet::::list_currencies(&account_id), + runtime_common::apis::RewardDomain::Block => pallet_rewards::Pallet::::list_currencies(&account_id), + runtime_common::apis::RewardDomain::Liquidity => pallet_rewards::Pallet::::list_currencies(&account_id), } } fn compute_reward(domain: runtime_common::apis::RewardDomain, currency_id: CurrencyId, account_id: AccountId) -> Option { match domain { - runtime_common::apis::RewardDomain::Block => as cfg_traits::rewards::AccountRewards>::compute_reward(currency_id, &account_id).ok(), - runtime_common::apis::RewardDomain::Liquidity => as cfg_traits::rewards::AccountRewards>::compute_reward(currency_id, &account_id).ok(), + runtime_common::apis::RewardDomain::Block => as cfg_traits::rewards::AccountRewards>::compute_reward(currency_id, &account_id).ok(), + runtime_common::apis::RewardDomain::Liquidity => as cfg_traits::rewards::AccountRewards>::compute_reward(currency_id, &account_id).ok(), } } } diff --git a/runtime/integration-tests/src/generic/cases/rewards.rs b/runtime/integration-tests/src/generic/cases/rewards.rs new file mode 100644 index 0000000000..3b7ee83c83 --- /dev/null +++ b/runtime/integration-tests/src/generic/cases/rewards.rs @@ -0,0 +1,51 @@ +use cfg_primitives::CFG; +use cfg_traits::rewards::{AccountRewards, CurrencyGroupChange, DistributedRewards}; +use cfg_types::tokens::CurrencyId; +use frame_support::assert_ok; +use runtime_common::apis::{runtime_decl_for_rewards_api::RewardsApiV1, RewardDomain}; +use sp_runtime::traits::Get; + +use crate::{ + generic::{config::Runtime, env::Env, envs::runtime_env::RuntimeEnv, utils}, + utils::accounts::Keyring, +}; + +type BlockRewards = pallet_rewards::Instance1; + +const STAKER: Keyring = Keyring::Alice; + +fn block_rewards_api() { + RuntimeEnv::::default().parachain_state_mut(|| { + let group_id = 1u32; + let amount = 100 * CFG; + + utils::give_balance::(STAKER.id(), T::ExistentialDeposit::get() + amount); + + assert_ok!(pallet_rewards::Pallet::::attach_currency( + CurrencyId::Native, + group_id, + )); + + assert_ok!(pallet_rewards::Pallet::::deposit_stake( + CurrencyId::Native, + &STAKER.id(), + amount, + )); + + assert_ok!( + pallet_rewards::Pallet::::distribute_reward(200 * CFG, [group_id]) + ); + + assert_eq!( + T::Api::list_currencies(RewardDomain::Block, STAKER.id()), + vec![CurrencyId::Native] + ); + + assert_eq!( + T::Api::compute_reward(RewardDomain::Block, CurrencyId::Native, STAKER.id()), + Some(200 * CFG) + ) + }); +} + +crate::test_for_runtimes!(all, block_rewards_api); diff --git a/runtime/integration-tests/src/generic/config.rs b/runtime/integration-tests/src/generic/config.rs index 5627c4561f..ff98123c3b 100644 --- a/runtime/integration-tests/src/generic/config.rs +++ b/runtime/integration-tests/src/generic/config.rs @@ -2,7 +2,7 @@ use std::fmt::Debug; use cfg_primitives::{ AccountId, Address, AuraId, Balance, BlockNumber, CollectionId, CouncilCollective, Header, - Index, ItemId, LoanId, OrderId, PoolId, Signature, TrancheId, + IBalance, Index, ItemId, LoanId, OrderId, PoolId, Signature, TrancheId, }; use cfg_traits::Millis; use cfg_types::{ @@ -29,11 +29,13 @@ use runtime_common::{ fees::{DealWithFees, WeightToFee}, oracle::Feeder, remarks::Remark, + rewards::SingleCurrencyMovement, }; use sp_core::H256; use sp_runtime::{ scale_info::TypeInfo, traits::{AccountIdLookup, Block, Dispatchable, Get, Member}, + FixedI128, }; /// Kind of runtime to check in runtime time @@ -149,6 +151,17 @@ pub trait Runtime: + pallet_evm_chain_id::Config + pallet_remarks::Config + pallet_utility::Config + + pallet_rewards::Config< + pallet_rewards::Instance1, + GroupId = u32, + CurrencyId = CurrencyId, + RewardMechanism = pallet_rewards::mechanism::base::Mechanism< + Balance, + IBalance, + FixedI128, + SingleCurrencyMovement, + >, + > { /// Just the RuntimeCall type, but redefined with extra bounds. /// You can add `From` bounds in order to convert pallet calls to @@ -270,6 +283,11 @@ pub trait Runtime: > + apis::runtime_decl_for_account_conversion_api::AccountConversionApiV1< Self::Block, AccountId, + > + apis::runtime_decl_for_rewards_api::RewardsApiV1< + Self::Block, + AccountId, + Balance, + CurrencyId, >; type MaxTranchesExt: Codec + Get + Member + PartialOrd + TypeInfo; diff --git a/runtime/integration-tests/src/generic/mod.rs b/runtime/integration-tests/src/generic/mod.rs index fb0fe1c517..5e0b2a3cdd 100644 --- a/runtime/integration-tests/src/generic/mod.rs +++ b/runtime/integration-tests/src/generic/mod.rs @@ -22,6 +22,7 @@ mod cases { mod oracles; mod proxy; mod restricted_transfers; + mod rewards; } /// Generate tests for the specified runtimes or all runtimes. diff --git a/runtime/integration-tests/src/lib.rs b/runtime/integration-tests/src/lib.rs index d86123f6f0..a4d349415f 100644 --- a/runtime/integration-tests/src/lib.rs +++ b/runtime/integration-tests/src/lib.rs @@ -17,7 +17,6 @@ mod evm; mod generic; mod rewards; -mod runtime_apis; mod utils; /// Re-exports the correct runtimes that we run the integration tests with diff --git a/runtime/integration-tests/src/runtime_apis/mod.rs b/runtime/integration-tests/src/runtime_apis/mod.rs deleted file mode 100644 index 26e584e939..0000000000 --- a/runtime/integration-tests/src/runtime_apis/mod.rs +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright 2021 Centrifuge Foundation (centrifuge.io). -// -// This file is part of the Centrifuge chain project. -// Centrifuge 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 (see http://www.gnu.org/licenses). -// Centrifuge 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. -mod rewards; - -use std::sync::Arc; - -use cfg_primitives::{AuraId, CFG}; -use cumulus_primitives_parachain_inherent::ParachainInherentData; -use frame_support::traits::GenesisBuild; -use fudge::{ - digest::{DigestCreator, DigestProvider, FudgeAuraDigest}, - inherent::{FudgeInherentParaParachain, FudgeInherentTimestamp}, - primitives::ParaId, - state::StateProvider, - StandaloneBuilder, TWasmExecutor, -}; -use sc_client_api::{HeaderBackend, StorageProof}; -use sc_executor::WasmExecutor; -use sc_service::{TFullBackend, TFullClient}; -use sp_api::ProvideRuntimeApi as _; -use sp_consensus_slots::SlotDuration; -use sp_core::{ - sr25519, - sr25519::{Pair, Public}, - Pair as TraitPair, H256, -}; -use sp_inherents::{CreateInherentDataProviders, InherentData}; -use sp_runtime::{ - generic::BlockId, - traits::{BlakeTwo256, IdentifyAccount}, - BuildStorage, Storage, -}; -use tokio::runtime::Handle; - -use crate::{ - chain::{ - centrifuge, - centrifuge::{Runtime, PARA_ID}, - }, - utils::accounts::Keyring, -}; - -/// Start date used for timestamps in test-enviornments -/// Sat Jan 01 2022 00:00:00 GMT+0000 -pub const START_DATE: u64 = 1640995200u64; - -/// The type that CreatesInherentDataProviders for the para-chain. -/// As a new-type here as otherwise the TestEnv is badly -/// readable. -#[allow(unused)] -type Cidp = Box< - dyn CreateInherentDataProviders< - centrifuge::Block, - (), - InherentDataProviders = ( - FudgeInherentTimestamp, - sp_consensus_aura::inherents::InherentDataProvider, - FudgeInherentParaParachain, - ), - >, ->; - -/// The type creates digests for the chains. -#[allow(unused)] -type Dp = Box + Send + Sync>; - -type ApiRef<'a> = sp_api::ApiRef<'a, as sp_api::ProvideRuntimeApi>::Api>; - -fn create_builder( - handle: Handle, - genesis: Option, -) -> StandaloneBuilder { - let mut state = - StateProvider::, centrifuge::Block>::empty_default(Some( - centrifuge::WASM_BINARY.expect("Wasm is build. Qed."), - )) - .expect("ESSENTIAL: State provider can be created."); - state.insert_storage( - pallet_aura::GenesisConfig:: { - authorities: vec![AuraId::from(sr25519::Public([0u8; 32]))], - } - .build_storage() - .expect("ESSENTIAL: GenesisBuild must not fail at this stage."), - ); - - state.insert_storage( - pallet_balances::GenesisConfig:: { - balances: vec![(Keyring::Alice.to_account_id(), 10000 * CFG)], - } - .build_storage() - .expect("ESSENTIAL: GenesisBuild must not fail at this stage."), - ); - - if let Some(storage) = genesis { - state.insert_storage(storage); - } - - let mut init = fudge::initiator::default(handle); - init.with_genesis(Box::new(state)); - - let para_id = ParaId::from(centrifuge::PARA_ID); - let instance_id = FudgeInherentTimestamp::create_instance( - std::time::Duration::from_secs(12), - Some(std::time::Duration::from_millis(START_DATE)), - ) - .expect("ESSENTIAL: Instance ID can be created."); - - let cidp = Box::new(move |_parent: H256, ()| { - async move { - let timestamp = FudgeInherentTimestamp::get_instance(instance_id) - .expect("Instances is initialized"); - - let slot = - sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( - timestamp.current_time(), - SlotDuration::from_millis(std::time::Duration::from_secs(12).as_millis() as u64), - ); - // Dummy data for relay-inherent - let inherent = ParachainInherentData { - validation_data: Default::default(), - relay_chain_state: StorageProof::empty(), - downward_messages: vec![], - horizontal_messages: Default::default(), - }; - let relay_para_inherent = FudgeInherentParaParachain::new(inherent); - Ok((timestamp, slot, relay_para_inherent)) - } - }); - - let dp = |clone_client: Arc< - TFullClient, - >| { - Box::new( - move |parent: sp_runtime::generic::Header, inherents| { - let client = clone_client.clone(); - - async move { - let aura = FudgeAuraDigest::< - centrifuge::Block, - TFullClient, - >::new(&*client) - .expect("ESSENTIAL: Aura digest can be created."); - - let digest = aura.build_digest(parent, &inherents).await?; - Ok(digest) - } - }, - ) - }; - - StandaloneBuilder::<_, _, Cidp, Dp>::new(init, |client| (cidp, dp(client))) - .expect("ESSENTIAL: Standalone builder can be created.") -} - -pub struct ApiEnv { - builder: StandaloneBuilder, -} - -impl ApiEnv { - pub fn new(handle: Handle) -> Self { - Self { - builder: create_builder(handle, Some(Storage::default())), - } - } - - pub fn new_with_genesis(handle: Handle, genesis: Storage) -> Self { - crate::utils::logs::init_logs(); - - Self { - builder: create_builder(handle, Some(genesis)), - } - } - - pub fn with_api(&self, exec: F) -> &Self - where - F: FnOnce(ApiRef, BlockId), - { - let client = self.builder.client(); - let api = client.runtime_api(); - let best_hash = BlockId::hash(self.builder.client().info().best_hash); - - exec(api, best_hash); - - self - } - - pub fn startup(&mut self, start_up: F) -> &mut Self - where - F: FnOnce(), - { - self.builder.with_mut_state(start_up).unwrap(); - - self - } -} diff --git a/runtime/integration-tests/src/runtime_apis/rewards.rs b/runtime/integration-tests/src/runtime_apis/rewards.rs deleted file mode 100644 index 058fa2c647..0000000000 --- a/runtime/integration-tests/src/runtime_apis/rewards.rs +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2021 Centrifuge Foundation (centrifuge.io). -// -// This file is part of the Centrifuge chain project. -// Centrifuge 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 (see http://www.gnu.org/licenses). -// Centrifuge 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. - -use cfg_primitives::{AccountId, Balance, CFG}; -use cfg_traits::rewards::{AccountRewards, CurrencyGroupChange, DistributedRewards, GroupRewards}; -use cfg_types::tokens::CurrencyId; -use development_runtime::BlockId; -use frame_support::assert_ok; -use runtime_common::apis::{RewardDomain, RewardsApi}; -use sp_core::{sr25519, Pair}; -use sp_runtime::traits::IdentifyAccount; -use tokio::runtime::Handle; - -use super::ApiEnv; -use crate::utils::accounts::Keyring; - -// #[tokio::test] -// async fn liquidity_rewards_runtime_api_works() { -// rewards_runtime_api_works::(RewardDomain::Liquidity) -// .await; -// } - -#[tokio::test] -async fn block_rewards_runtime_api_works() { - rewards_runtime_api_works::(RewardDomain::Block).await; -} - -type GroupId = u32; - -async fn rewards_runtime_api_works(domain: RewardDomain) -where - Rewards: CurrencyGroupChange - + AccountRewards - + DistributedRewards - + GroupRewards, -{ - let staker = Keyring::Alice.to_account_id(); - let expected_reward = 200 * CFG; - ApiEnv::new(Handle::current()) - .startup(|| { - let currencies = vec![(CurrencyId::Native, 1u32)]; - let stake_accounts = vec![(staker.clone(), CurrencyId::Native, 100 * CFG)]; - let rewards = vec![(1, expected_reward)]; - - for (currency_id, group_id) in currencies { - ::attach_currency(currency_id, group_id) - .expect("Attaching currency should work"); - } - - for (account_id, currency_id, amount) in stake_accounts { - >::deposit_stake( - currency_id, - &account_id, - amount, - ) - .expect("Depositing stake should work"); - } - - for (group_id, amount) in &rewards { - ::distribute_reward(*amount, [*group_id]) - .expect("Distributing rewards should work"); - } - - if let RewardDomain::Liquidity = domain { - /// For the gap mechanism, used by liquidity rewards, - /// we need another distribution to allow the participant claim - /// rewards - for (group_id, amount) in &rewards { - let res = - ::distribute_reward(*amount, [*group_id]) - .expect("Distributing rewards should work"); - - res.iter().for_each(|item| { - item.expect("Rewards distribution error"); - }); - } - } - }) - .with_api(|api, latest| { - let hash = match latest { - BlockId::Hash(hash) => hash, - BlockId::Number(n) => todo!("nuno"), - }; - - let currencies = api - .list_currencies(hash.clone(), domain, staker.clone()) - .expect("There should be staked currencies"); - assert_eq!(currencies.clone().len(), 1); - - let currency_id = currencies[0]; - - let reward = api - .compute_reward(hash, domain, currency_id, staker) - .unwrap(); - assert_eq!(reward, Some(expected_reward)); - }); -}