diff --git a/pallets/pallet-bonded-coins/src/lib.rs b/pallets/pallet-bonded-coins/src/lib.rs index e8ae6641f..5dc595c01 100644 --- a/pallets/pallet-bonded-coins/src/lib.rs +++ b/pallets/pallet-bonded-coins/src/lib.rs @@ -557,7 +557,8 @@ pub mod pallet { /// - `origin`: The origin of the call, requiring the caller to be a /// manager of the pool. /// - `pool_id`: The identifier of the pool to be locked. - /// - `lock`: The locks to be applied to the pool. + /// - `lock`: The locks to be applied to the pool. At least one lock + /// flag must be set to `false` for a valid lock. /// /// # Returns /// - `DispatchResult`: The result of the dispatch. @@ -568,11 +569,16 @@ pub mod pallet { /// pool. /// - `Error::::PoolNotLive`: If the pool is not in a live (locked or /// active) state. + /// - `Error::::InvalidInput`: If all lock flags are `true` (all + /// operations enabled), which would be equivalent to an unlocked + /// (active) pool state. #[pallet::call_index(3)] #[pallet::weight(T::WeightInfo::set_lock())] pub fn set_lock(origin: OriginFor, pool_id: T::PoolId, lock: Locks) -> DispatchResult { let who = T::DefaultOrigin::ensure_origin(origin)?; + ensure!(lock.any_lock_set(), Error::::InvalidInput); + Pools::::try_mutate(&pool_id, |pool| -> DispatchResult { let entry = pool.as_mut().ok_or(Error::::PoolUnknown)?; ensure!(entry.state.is_live(), Error::::PoolNotLive); diff --git a/pallets/pallet-bonded-coins/src/tests/transactions/set_lock.rs b/pallets/pallet-bonded-coins/src/tests/transactions/set_lock.rs index c3c6b4058..3e2f14748 100644 --- a/pallets/pallet-bonded-coins/src/tests/transactions/set_lock.rs +++ b/pallets/pallet-bonded-coins/src/tests/transactions/set_lock.rs @@ -116,6 +116,45 @@ fn set_lock_works_when_locked() { }); } +#[test] +fn set_lock_requires_at_least_one_flag_set() { + let pool_details = generate_pool_details( + vec![DEFAULT_BONDED_CURRENCY_ID], + get_linear_bonding_curve(), + true, + Some(PoolStatus::Active), + Some(ACCOUNT_00), + Some(DEFAULT_COLLATERAL_CURRENCY_ID), + Some(ACCOUNT_00), + None, + ); + let pool_id: AccountIdOf = calculate_pool_id(&[DEFAULT_BONDED_CURRENCY_ID]); + + ExtBuilder::default() + .with_pools(vec![(pool_id.clone(), pool_details)]) + .with_native_balances(vec![(ACCOUNT_00, ONE_HUNDRED_KILT)]) + .with_collaterals(vec![DEFAULT_COLLATERAL_CURRENCY_ID]) + .with_bonded_balance(vec![ + (DEFAULT_COLLATERAL_CURRENCY_ID, pool_id.clone(), u128::MAX / 10), + (DEFAULT_BONDED_CURRENCY_ID, ACCOUNT_00, u128::MAX / 10), + ]) + .build_and_execute_with_sanity_tests(|| { + let origin = RawOrigin::Signed(ACCOUNT_00).into(); + + assert_err!( + BondingPallet::set_lock( + origin, + pool_id.clone(), + Locks { + allow_burn: true, + allow_mint: true + } + ), + Error::::InvalidInput + ); + }); +} + #[test] fn set_lock_fails_when_not_authorized() { let pool_details = generate_pool_details( diff --git a/pallets/pallet-bonded-coins/src/types.rs b/pallets/pallet-bonded-coins/src/types.rs index 00c223e03..12fe093e3 100644 --- a/pallets/pallet-bonded-coins/src/types.rs +++ b/pallets/pallet-bonded-coins/src/types.rs @@ -25,6 +25,12 @@ pub struct Locks { pub allow_burn: bool, } +impl Locks { + pub const fn any_lock_set(&self) -> bool { + !(self.allow_mint && self.allow_burn) + } +} + /// Status of a pool. #[derive(Clone, Encode, Decode, PartialEq, Eq, TypeInfo, MaxEncodedLen, Debug)] pub enum PoolStatus {