diff --git a/pallets/pool-registry/src/mock.rs b/pallets/pool-registry/src/mock.rs index 895ff43d84..3819b2fa27 100644 --- a/pallets/pool-registry/src/mock.rs +++ b/pallets/pool-registry/src/mock.rs @@ -33,7 +33,7 @@ use frame_support::{ dispatch::DispatchResult, pallet_prelude::DispatchError, parameter_types, - traits::{Contains, Hooks, PalletInfoAccess, SortedMembers}, + traits::{Contains, EnsureOriginWithArg, Hooks, PalletInfoAccess, SortedMembers}, PalletId, }; use frame_system::EnsureSigned; @@ -115,7 +115,22 @@ impl cfg_test_utils::mocks::nav::Config for Test { type PoolId = PoolId; } +pub struct All; +impl EnsureOriginWithArg for All { + type Success = (); + + fn try_origin(_: RuntimeOrigin, _: &PoolId) -> Result { + Ok(()) + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin(_: &PoolId) -> Result { + Ok(RuntimeOrigin::root()) + } +} + impl pallet_pool_system::Config for Test { + type AdminOrigin = All; type AssetRegistry = RegistryMock; type AssetsUnderManagementNAV = FakeNav; type Balance = Balance; diff --git a/pallets/pool-system/src/lib.rs b/pallets/pool-system/src/lib.rs index 55f947fe01..2ad9da3ff4 100644 --- a/pallets/pool-system/src/lib.rs +++ b/pallets/pool-system/src/lib.rs @@ -184,7 +184,7 @@ pub mod pallet { use frame_support::{ pallet_prelude::*, sp_runtime::traits::Convert, - traits::{tokens::Preservation, Contains}, + traits::{tokens::Preservation, Contains, EnsureOriginWithArg}, PalletId, }; use sp_runtime::{traits::BadOrigin, ArithmeticError}; @@ -195,6 +195,8 @@ pub mod pallet { pub trait Config: frame_system::Config { type RuntimeEvent: From> + IsType<::RuntimeEvent>; + type AdminOrigin: EnsureOriginWithArg; + type Balance: Member + Parameter + AtLeast32BitUnsigned @@ -613,15 +615,7 @@ pub mod pallet { #[transactional] #[pallet::call_index(1)] pub fn close_epoch(origin: OriginFor, pool_id: T::PoolId) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin)?; - ensure!( - T::Permission::has( - PermissionScope::Pool(pool_id), - who, - Role::PoolRole(PoolRole::LiquidityAdmin) - ), - BadOrigin - ); + T::AdminOrigin::ensure_origin(origin, &pool_id)?; Pool::::try_mutate(pool_id, |pool| { let pool = pool.as_mut().ok_or(Error::::NoSuchPool)?; @@ -884,15 +878,7 @@ pub mod pallet { origin: OriginFor, pool_id: T::PoolId, ) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin)?; - ensure!( - T::Permission::has( - PermissionScope::Pool(pool_id), - who, - Role::PoolRole(PoolRole::LiquidityAdmin) - ), - BadOrigin - ); + T::AdminOrigin::ensure_origin(origin, &pool_id)?; EpochExecution::::try_mutate(pool_id, |epoch_info| { let epoch = epoch_info diff --git a/pallets/pool-system/src/mock.rs b/pallets/pool-system/src/mock.rs index c1d18709fc..58b0f5e669 100644 --- a/pallets/pool-system/src/mock.rs +++ b/pallets/pool-system/src/mock.rs @@ -27,8 +27,10 @@ use cfg_types::{ tokens::{CurrencyId, CustomMetadata, TrancheCurrency}, }; use frame_support::{ - assert_ok, derive_impl, parameter_types, - traits::{Contains, Hooks, PalletInfoAccess, SortedMembers}, + assert_ok, derive_impl, + dispatch::RawOrigin, + parameter_types, + traits::{Contains, EnsureOriginWithArg, Hooks, PalletInfoAccess, SortedMembers}, Blake2_128, PalletId, StorageHasher, }; use frame_system::{EnsureSigned, EnsureSignedBy}; @@ -37,11 +39,8 @@ use pallet_pool_fees::PoolFeeInfoOf; use pallet_restricted_tokens::TransferDetails; use parity_scale_codec::Encode; use sp_arithmetic::FixedPointNumber; -use sp_core::H256; -use sp_runtime::{ - traits::{ConstU128, Zero}, - BuildStorage, -}; +use sp_core::{ConstU128, H256}; +use sp_runtime::{traits::Zero, BuildStorage}; use sp_std::marker::PhantomData; use crate::{ @@ -385,7 +384,34 @@ parameter_types! { pub const PoolDeposit: Balance = 1 * CURRENCY; } +pub struct LiquidityAndPoolAdmin; +impl EnsureOriginWithArg for LiquidityAndPoolAdmin { + type Success = (); + + fn try_origin(o: RuntimeOrigin, _: &PoolId) -> Result { + , RuntimeOrigin>>>::into(o).and_then( + |r| match r { + RawOrigin::Root => Ok(()), + RawOrigin::Signed(account) => { + if account == DEFAULT_POOL_OWNER { + Ok(()) + } else { + Err(RawOrigin::Signed(account).into()) + } + } + RawOrigin::None => Err(RawOrigin::None.into()), + }, + ) + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin(_: &PoolId) -> Result { + Ok(RuntimeOrigin::Signed(DEFAULT_POOL_OWNER)) + } +} + impl Config for Runtime { + type AdminOrigin = LiquidityAndPoolAdmin; type AssetRegistry = RegistryMock; type AssetsUnderManagementNAV = FakeNav; type Balance = Balance; diff --git a/pallets/pool-system/src/tests/mod.rs b/pallets/pool-system/src/tests/mod.rs index b9bcb2d916..4f550c9da9 100644 --- a/pallets/pool-system/src/tests/mod.rs +++ b/pallets/pool-system/src/tests/mod.rs @@ -506,7 +506,7 @@ fn pool_constraints_pass() { #[test] fn epoch() { new_test_ext().execute_with(|| { - let pool_owner = 2_u64; + let pool_owner = DEFAULT_POOL_OWNER; let pool_owner_origin = RuntimeOrigin::signed(pool_owner); let borrower = 3; @@ -744,7 +744,7 @@ fn epoch() { #[test] fn submission_period() { new_test_ext().execute_with(|| { - let pool_owner = 2_u64; + let pool_owner = DEFAULT_POOL_OWNER; let pool_owner_origin = RuntimeOrigin::signed(pool_owner); // Initialize pool with initial investments @@ -932,7 +932,7 @@ fn submission_period() { #[test] fn execute_info_removed_after_epoch_execute() { new_test_ext().execute_with(|| { - let pool_owner = 2_u64; + let pool_owner = DEFAULT_POOL_OWNER; let pool_owner_origin = RuntimeOrigin::signed(pool_owner); // Initialize pool with initial investments @@ -1021,7 +1021,7 @@ fn execute_info_removed_after_epoch_execute() { #[test] fn pool_updates_should_be_constrained() { new_test_ext().execute_with(|| { - let pool_owner = 0_u64; + let pool_owner = DEFAULT_POOL_OWNER; let pool_owner_origin = RuntimeOrigin::signed(pool_owner); let pool_id = 0; @@ -1556,7 +1556,7 @@ fn valid_tranche_structure_is_enforced() { #[test] fn triger_challange_period_with_zero_solution() { new_test_ext().execute_with(|| { - let pool_owner = 2_u64; + let pool_owner = DEFAULT_POOL_OWNER; let pool_owner_origin = RuntimeOrigin::signed(pool_owner); // Initialize pool with initial investments @@ -1650,7 +1650,7 @@ fn triger_challange_period_with_zero_solution() { #[test] fn min_challenge_time_is_respected() { new_test_ext().execute_with(|| { - let pool_owner = 2_u64; + let pool_owner = DEFAULT_POOL_OWNER; let pool_owner_origin = RuntimeOrigin::signed(pool_owner); // Initialize pool with initial investments @@ -1747,7 +1747,7 @@ fn min_challenge_time_is_respected() { #[test] fn only_zero_solution_is_accepted_max_reserve_violated() { new_test_ext().execute_with(|| { - let pool_owner = 2_u64; + let pool_owner = DEFAULT_POOL_OWNER; let pool_owner_origin = RuntimeOrigin::signed(pool_owner); // Initialize pool with initial investments @@ -1948,7 +1948,7 @@ fn only_zero_solution_is_accepted_max_reserve_violated() { #[test] fn only_zero_solution_is_accepted_when_risk_buff_violated_else() { new_test_ext().execute_with(|| { - let pool_owner = 2_u64; + let pool_owner = DEFAULT_POOL_OWNER; let pool_owner_origin = RuntimeOrigin::signed(pool_owner); // Initialize pool with initial investments @@ -2138,7 +2138,7 @@ fn only_zero_solution_is_accepted_when_risk_buff_violated_else() { #[test] fn only_usd_as_pool_currency_allowed() { new_test_ext().execute_with(|| { - let pool_owner = 2_u64; + let pool_owner = DEFAULT_POOL_OWNER; // Initialize pool with initial investments let senior_interest_rate = Rate::saturating_from_rational(10, 100) @@ -2331,7 +2331,7 @@ fn creation_takes_deposit() { // Pool creation one: // Owner 2, first deposit // total deposit for this owner is 1 - let pool_owner = 2_u64; + let pool_owner = DEFAULT_POOL_OWNER; assert_ok!(PoolSystem::create( pool_owner.clone(), @@ -2742,7 +2742,6 @@ mod pool_fees { use super::*; use crate::{mock::default_pool_fees, Event}; - const POOL_OWNER: AccountId = 2; const INVESTMENT_AMOUNT: Balance = DEFAULT_POOL_MAX_RESERVE / 10; const NAV_AMOUNT: Balance = INVESTMENT_AMOUNT / 2 + 2_345_000; const FEE_AMOUNT_FIXED: Balance = NAV_AMOUNT / 10; @@ -2761,8 +2760,8 @@ mod pool_fees { let senior_interest_rate = interest_rate / Rate::saturating_from_integer(SECONDS_PER_YEAR) + One::one(); assert_ok!(PoolSystem::create( - POOL_OWNER, - POOL_OWNER, + DEFAULT_POOL_OWNER, + DEFAULT_POOL_OWNER, DEFAULT_POOL_ID, vec![ TrancheInput { @@ -2854,11 +2853,11 @@ mod pool_fees { INVESTMENT_AMOUNT )); assert_ok!(PoolSystem::close_epoch( - RuntimeOrigin::signed(POOL_OWNER), + RuntimeOrigin::signed(DEFAULT_POOL_OWNER), 0 )); assert_ok!(PoolSystem::submit_solution( - RuntimeOrigin::signed(POOL_OWNER), + RuntimeOrigin::signed(DEFAULT_POOL_OWNER), DEFAULT_POOL_ID, vec![ TrancheSolution { @@ -2874,7 +2873,7 @@ mod pool_fees { // Execute epoch 1 should reduce reserve due to redemption assert_ok!(PoolSystem::execute_epoch( - RuntimeOrigin::signed(POOL_OWNER), + RuntimeOrigin::signed(DEFAULT_POOL_OWNER), DEFAULT_POOL_ID )); assert!(!EpochExecution::::contains_key(DEFAULT_POOL_ID)); @@ -2902,7 +2901,7 @@ mod pool_fees { // Closing epoch 2 should not change anything but reserve.available next_block(); assert_ok!(PoolSystem::close_epoch( - RuntimeOrigin::signed(POOL_OWNER), + RuntimeOrigin::signed(DEFAULT_POOL_OWNER), 0 )); assert_eq!( @@ -2985,7 +2984,7 @@ mod pool_fees { )); next_block(); assert_ok!(PoolSystem::close_epoch( - RuntimeOrigin::signed(POOL_OWNER), + RuntimeOrigin::signed(DEFAULT_POOL_OWNER), 0 )); assert_eq!( @@ -3038,7 +3037,7 @@ mod pool_fees { // Executing epoch should reduce FeeNav by disbursement and transfer from // PoolFees account to destination assert_ok!(PoolSystem::submit_solution( - RuntimeOrigin::signed(POOL_OWNER), + RuntimeOrigin::signed(DEFAULT_POOL_OWNER), DEFAULT_POOL_ID, vec![ TrancheSolution { @@ -3052,7 +3051,7 @@ mod pool_fees { ] )); assert_ok!(PoolSystem::execute_epoch( - RuntimeOrigin::signed(POOL_OWNER), + RuntimeOrigin::signed(DEFAULT_POOL_OWNER), DEFAULT_POOL_ID )); assert!(!EpochExecution::::contains_key(DEFAULT_POOL_ID)); @@ -3087,7 +3086,7 @@ mod pool_fees { next_block(); test_nav_up(DEFAULT_POOL_ID, new_nav_amount - NAV_AMOUNT); assert_ok!(PoolSystem::close_epoch( - RuntimeOrigin::signed(POOL_OWNER), + RuntimeOrigin::signed(DEFAULT_POOL_OWNER), 0 )); @@ -3140,7 +3139,7 @@ mod pool_fees { // NAV = 0 + AUM - PoolFeesNAV = -AUM assert_ok!(PoolSystem::close_epoch( - RuntimeOrigin::signed(POOL_OWNER), + RuntimeOrigin::signed(DEFAULT_POOL_OWNER), 0 )); assert!(System::events().iter().any(|e| match e.event { @@ -3209,7 +3208,7 @@ mod pool_fees { // Closing should update fee nav next_block(); assert_ok!(PoolSystem::close_epoch( - RuntimeOrigin::signed(POOL_OWNER), + RuntimeOrigin::signed(DEFAULT_POOL_OWNER), 0 )); let fee_amount_from_charge = @@ -3241,7 +3240,7 @@ mod pool_fees { // Executin should reduce fee_nav by disbursement and transfer assert_ok!(PoolSystem::submit_solution( - RuntimeOrigin::signed(POOL_OWNER), + RuntimeOrigin::signed(DEFAULT_POOL_OWNER), DEFAULT_POOL_ID, vec![ TrancheSolution { @@ -3255,7 +3254,7 @@ mod pool_fees { ] )); assert_ok!(PoolSystem::execute_epoch( - RuntimeOrigin::signed(POOL_OWNER), + RuntimeOrigin::signed(DEFAULT_POOL_OWNER), DEFAULT_POOL_ID )); assert_eq!( @@ -3327,7 +3326,7 @@ mod pool_fees { // Closing should update fee nav next_block(); assert_ok!(PoolSystem::close_epoch( - RuntimeOrigin::signed(POOL_OWNER), + RuntimeOrigin::signed(DEFAULT_POOL_OWNER), 0 )); assert_eq!( @@ -3344,7 +3343,7 @@ mod pool_fees { // by fees assert_noop!( PoolSystem::submit_solution( - RuntimeOrigin::signed(POOL_OWNER), + RuntimeOrigin::signed(DEFAULT_POOL_OWNER), DEFAULT_POOL_ID, vec![ TrancheSolution { @@ -3360,7 +3359,7 @@ mod pool_fees { Error::::InsufficientCurrency ); assert_ok!(PoolSystem::submit_solution( - RuntimeOrigin::signed(POOL_OWNER), + RuntimeOrigin::signed(DEFAULT_POOL_OWNER), DEFAULT_POOL_ID, vec![ TrancheSolution { @@ -3374,7 +3373,7 @@ mod pool_fees { ] )); assert_ok!(PoolSystem::execute_epoch( - RuntimeOrigin::signed(POOL_OWNER), + RuntimeOrigin::signed(DEFAULT_POOL_OWNER), DEFAULT_POOL_ID )); assert_pending_fees(DEFAULT_POOL_ID, fees.clone(), vec![(fee_nav, 0, None)]); diff --git a/runtime/altair/src/lib.rs b/runtime/altair/src/lib.rs index 69a540163a..b112e308cd 100644 --- a/runtime/altair/src/lib.rs +++ b/runtime/altair/src/lib.rs @@ -1455,6 +1455,7 @@ parameter_types! { } impl pallet_pool_system::Config for Runtime { + type AdminOrigin = runtime_common::pool::LiquidityAndPoolAdminOrRoot; type AssetRegistry = OrmlAssetRegistry; type AssetsUnderManagementNAV = Loans; type Balance = Balance; diff --git a/runtime/centrifuge/src/lib.rs b/runtime/centrifuge/src/lib.rs index 722a5174be..44bc895d62 100644 --- a/runtime/centrifuge/src/lib.rs +++ b/runtime/centrifuge/src/lib.rs @@ -1463,6 +1463,7 @@ impl pallet_pool_registry::Config for Runtime { } impl pallet_pool_system::Config for Runtime { + type AdminOrigin = runtime_common::pool::LiquidityAndPoolAdminOrRoot; type AssetRegistry = OrmlAssetRegistry; type AssetsUnderManagementNAV = Loans; type Balance = Balance; diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 87738b8fa8..763cebbb92 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -23,6 +23,7 @@ pub mod fees; pub mod gateway; pub mod migrations; pub mod oracle; +pub mod pool; pub mod remarks; pub mod transfer_filter; pub mod xcm; diff --git a/runtime/common/src/pool.rs b/runtime/common/src/pool.rs new file mode 100644 index 0000000000..904f06d3ef --- /dev/null +++ b/runtime/common/src/pool.rs @@ -0,0 +1,62 @@ +// Copyright 2021 Centrifuge Foundation (centrifuge.io). +// This file is part of 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_traits::Permissions; +use cfg_types::permissions::{PermissionScope, PoolRole, Role}; +use frame_support::{dispatch::RawOrigin, traits::EnsureOriginWithArg}; + +pub struct LiquidityAndPoolAdminOrRoot(sp_std::marker::PhantomData); + +impl< + T: frame_system::Config + + pallet_permissions::Config< + Scope = PermissionScope, + Role = Role, + > + pallet_pool_system::Config, + > EnsureOriginWithArg for LiquidityAndPoolAdminOrRoot +where + ::RuntimeOrigin: From::AccountId>> + + Into::AccountId>, T::RuntimeOrigin>>, +{ + type Success = (); + + fn try_origin( + o: T::RuntimeOrigin, + pool_id: &T::PoolId, + ) -> Result { + o.into().and_then(|r| match r { + RawOrigin::Root => Ok(()), + RawOrigin::Signed(by) => { + if as Permissions>::has( + PermissionScope::Pool(*pool_id), + by.clone(), + Role::PoolRole(PoolRole::PoolAdmin), + ) || as Permissions>::has( + PermissionScope::Pool(*pool_id), + by.clone(), + Role::PoolRole(PoolRole::LiquidityAdmin), + ) { + Ok(()) + } else { + Err(T::RuntimeOrigin::from(RawOrigin::Signed(by))) + } + } + RawOrigin::None => Err(T::RuntimeOrigin::from(RawOrigin::None)), + }) + } + + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin(_: &T::PoolId) -> Result { + Ok(T::RuntimeOrigin::root()) + } +} diff --git a/runtime/development/src/lib.rs b/runtime/development/src/lib.rs index 7bb2cde05f..8c6a61bb4a 100644 --- a/runtime/development/src/lib.rs +++ b/runtime/development/src/lib.rs @@ -1041,6 +1041,7 @@ parameter_types! { } impl pallet_pool_system::Config for Runtime { + type AdminOrigin = runtime_common::pool::LiquidityAndPoolAdminOrRoot; type AssetRegistry = OrmlAssetRegistry; type AssetsUnderManagementNAV = Loans; type Balance = Balance;