Skip to content

Commit

Permalink
Add dynamic BurnParameters for Kusama Treasury (#511)
Browse files Browse the repository at this point in the history
This Pull Request resolves the signal raised by the Kusama [Ref.
#437](https://kusama.subsquare.io/referenda/437), setting both `Burn`
and `BurnDestination` parameters in Kusama's `pallet-treasury` as a
structure (both of them need to be set to prevent some unwanted
behaviour) that can be parametrized via OpenGov.

Default (when set to `None`) configuration behaves like what's already
out there: 0%, no account.

- [ ] Does not require a CHANGELOG entry
  • Loading branch information
pandres95 authored Jan 14, 2025
1 parent c5e9f58 commit 700417a
Show file tree
Hide file tree
Showing 3 changed files with 224 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Changed

- Kusama Treasury: remove funding to the Kappa Sigma Mu Society and disable burn ([polkadot-fellows/runtimes#507](https://github.com/polkadot-fellows/runtimes/pull/507))
- Kusama Treasury: allow burn parameters to be set via OpenGov ([polkadot-fellows/runtimes#511](https://github.com/polkadot-fellows/runtimes/pull/511))
- Remove Snowbridge create agent and channel extrinsics. ([polkadot-fellows/runtimes#506](https://github.com/polkadot-fellows/runtimes/pull/506))

#### From [#490](https://github.com/polkadot-fellows/runtimes/pull/490)
Expand Down
61 changes: 57 additions & 4 deletions relay/kusama/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ extern crate alloc;
use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::{
dynamic_params::{dynamic_pallet_params, dynamic_params},
traits::EnsureOriginWithArg,
traits::{EnsureOrigin, EnsureOriginWithArg},
weights::constants::{WEIGHT_PROOF_SIZE_PER_KB, WEIGHT_REF_TIME_PER_MICROS},
};
use kusama_runtime_constants::system_parachain::coretime::TIMESLICE_PERIOD;
Expand Down Expand Up @@ -641,6 +641,15 @@ impl pallet_bags_list::Config<VoterBagsListInstance> for Runtime {
type Score = sp_npos_elections::VoteWeight;
}

#[derive(Default, MaxEncodedLen, Encode, Decode, TypeInfo, Clone, Eq, PartialEq, Debug)]
pub struct BurnDestinationAccount(pub Option<AccountId>);

impl BurnDestinationAccount {
pub fn is_set(&self) -> bool {
self.0.is_some()
}
}

/// Dynamic params that can be adjusted at runtime.
#[dynamic_params(RuntimeParameters, pallet_parameters::Parameters::<Runtime>)]
pub mod dynamic_params {
Expand Down Expand Up @@ -678,6 +687,17 @@ pub mod dynamic_params {
#[codec(index = 4)]
pub static UseAuctionSlots: bool = true;
}

/// Parameters used by `pallet-treasury` to handle the burn process.
#[dynamic_pallet_params]
#[codec(index = 1)]
pub mod treasury {
#[codec(index = 0)]
pub static BurnPortion: Permill = Permill::from_percent(0);

#[codec(index = 1)]
pub static BurnDestination: BurnDestinationAccount = Default::default();
}
}

#[cfg(feature = "runtime-benchmarks")]
Expand All @@ -703,6 +723,8 @@ impl EnsureOriginWithArg<RuntimeOrigin, RuntimeParametersKey> for DynamicParamet

match key {
Inflation(_) => frame_system::ensure_root(origin.clone()),
Treasury(_) =>
EitherOf::<EnsureRoot<AccountId>, GeneralAdmin>::ensure_origin(origin.clone()),
}
.map_err(|_| origin)
}
Expand Down Expand Up @@ -828,7 +850,6 @@ parameter_types! {
pub const ProposalBondMinimum: Balance = 2000 * CENTS;
pub const ProposalBondMaximum: Balance = GRAND;
pub const SpendPeriod: BlockNumber = 6 * DAYS;
pub const Burn: Permill = Permill::zero();
pub const TreasuryPalletId: PalletId = PalletId(*b"py/trsry");
pub const PayoutSpendPeriod: BlockNumber = 30 * DAYS;
// The asset's interior location for the paying account. This is the Treasury
Expand All @@ -845,14 +866,46 @@ parameter_types! {
pub const MaxPeerInHeartbeats: u32 = 10_000;
}

use frame_support::traits::{Currency, OnUnbalanced};

pub type BalancesNegativeImbalance = <Balances as Currency<AccountId>>::NegativeImbalance;
pub struct TreasuryBurnHandler;

impl OnUnbalanced<BalancesNegativeImbalance> for TreasuryBurnHandler {
fn on_nonzero_unbalanced(amount: BalancesNegativeImbalance) {
let destination = dynamic_params::treasury::BurnDestination::get();

if let BurnDestinationAccount(Some(account)) = destination {
// Must resolve into existing but better to be safe.
Balances::resolve_creating(&account, amount);
} else {
// If no account to destinate the funds to, just drop the
// imbalance.
<() as OnUnbalanced<_>>::on_nonzero_unbalanced(amount)
}
}
}

impl Get<Permill> for TreasuryBurnHandler {
fn get() -> Permill {
let destination = dynamic_params::treasury::BurnDestination::get();

if destination.is_set() {
dynamic_params::treasury::BurnPortion::get()
} else {
Permill::zero()
}
}
}

impl pallet_treasury::Config for Runtime {
type PalletId = TreasuryPalletId;
type Currency = Balances;
type RejectOrigin = EitherOfDiverse<EnsureRoot<AccountId>, Treasurer>;
type RuntimeEvent = RuntimeEvent;
type SpendPeriod = SpendPeriod;
type Burn = Burn;
type BurnDestination = ();
type Burn = TreasuryBurnHandler;
type BurnDestination = TreasuryBurnHandler;
type MaxApprovals = MaxApprovals;
type WeightInfo = weights::pallet_treasury::WeightInfo<Runtime>;
type SpendFunds = Bounties;
Expand Down
166 changes: 166 additions & 0 deletions relay/kusama/tests/treasury_burn_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Polkadot.

// Polkadot 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.

// Polkadot 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 Polkadot. If not, see <http://www.gnu.org/licenses/>.

//! TreasuryBurnHandler helper structure tests.
//!
//! Note: These tests emulate the effects of burning some amount on `pallet_treasury` via
//! [`OnUnbalanced`], not the behaviour itself.
use frame_support::{
parameter_types,
traits::{
tokens::fungible::{Inspect, Mutate},
Currency, ExistenceRequirement, Get, Imbalance, OnUnbalanced, WithdrawReasons,
},
};
use kusama_runtime_constants::currency::UNITS;
use polkadot_primitives::{AccountId, Balance};
use sp_arithmetic::Permill;
use staging_kusama_runtime::{
dynamic_params::treasury::{self, BurnDestination, BurnPortion},
Balances, BurnDestinationAccount, Parameters, RuntimeOrigin, RuntimeParameters, Treasury,
TreasuryBurnHandler,
};

parameter_types! {
TreasuryAccount: AccountId = Treasury::account_id();
}

const BURN_DESTINATION_ACCOUNT: AccountId = AccountId::new([1u8; 32]);

const TREASURY_AMOUNT: Balance = 10 * UNITS;
const SURPLUS: Balance = UNITS;

fn test(pre: impl FnOnce(), test: impl FnOnce(Balance)) {
sp_io::TestExternalities::default().execute_with(|| {
pre();

Balances::set_balance(&TreasuryAccount::get(), TREASURY_AMOUNT);

let amount_to_handle = TreasuryBurnHandler::get() * SURPLUS;
let burn = <Balances as Currency<_>>::withdraw(
&TreasuryAccount::get(),
SURPLUS,
WithdrawReasons::RESERVE,
ExistenceRequirement::KeepAlive,
)
.expect("withdrawing of `burn` is within balance limits; qed");

// Withdrawn surplus to burn it.
assert_eq!(Balances::balance(&TreasuryAccount::get()), TREASURY_AMOUNT - SURPLUS);

let (credit, burn) = burn.split(amount_to_handle);

// Burn amount that's not to handle.
<() as OnUnbalanced<_>>::on_unbalanced(burn);

assert_eq!(Balances::total_issuance(), TREASURY_AMOUNT - (SURPLUS - amount_to_handle));

// Let's handle the `credit`
TreasuryBurnHandler::on_unbalanced(credit);

test(amount_to_handle);

// Only the amount to handle was transferred to the burn destination account
// let burn_destination_account = BurnDestination::get();
let burn_destination_account = BURN_DESTINATION_ACCOUNT;
let burn_destination_account_balance =
<Balances as Inspect<_>>::total_balance(&burn_destination_account);

assert_eq!(burn_destination_account_balance, amount_to_handle);
})
}

#[test]
fn on_burn_parameters_not_set_does_not_handle_burn() {
test(
|| {},
|amount_to_handle| {
// Amount to burn should be zero by default
assert_eq!(amount_to_handle, 0);
},
)
}

#[test]
fn on_burn_portion_not_set_does_not_handle_burn() {
test(
|| {
Parameters::set_parameter(
RuntimeOrigin::root(),
RuntimeParameters::Treasury(treasury::Parameters::BurnDestination(
BurnDestination,
Some(BurnDestinationAccount(Some(BURN_DESTINATION_ACCOUNT))),
)),
)
.expect("parameters are set accordingly; qed");
},
|amount_to_handle| {
// Amount to burn should be zero by default
assert_eq!(amount_to_handle, 0);
},
)
}

#[test]
fn on_burn_destination_not_set_does_not_handle_burn() {
let one_percent = Permill::from_percent(1);
test(
|| {
Parameters::set_parameter(
RuntimeOrigin::root(),
RuntimeParameters::Treasury(treasury::Parameters::BurnPortion(
BurnPortion,
Some(one_percent),
)),
)
.expect("parameters are set accordingly; qed");
},
|amount_to_handle| {
// Amount to burn should be zero by default
assert_eq!(amount_to_handle, 0);
},
)
}

#[test]
fn on_burn_parameters_set_works() {
let one_percent = Permill::from_percent(1);
test(
|| {
Parameters::set_parameter(
RuntimeOrigin::root(),
RuntimeParameters::Treasury(treasury::Parameters::BurnDestination(
BurnDestination,
Some(BurnDestinationAccount(Some(BURN_DESTINATION_ACCOUNT))),
)),
)
.expect("parameters are set accordingly; qed");
Parameters::set_parameter(
RuntimeOrigin::root(),
RuntimeParameters::Treasury(treasury::Parameters::BurnPortion(
BurnPortion,
Some(one_percent),
)),
)
.expect("parameters are set accordingly; qed");
},
|amount_to_handle| {
// Amount to burn should be zero by default
assert_eq!(amount_to_handle, one_percent * SURPLUS);
},
)
}

0 comments on commit 700417a

Please sign in to comment.