From 7d2ecc89954a0b4c96a428ee981814ea401f4507 Mon Sep 17 00:00:00 2001
From: Doordashcon <jesse.chejieh@gmail.com>
Date: Thu, 23 Jun 2022 05:18:40 +0100
Subject: [PATCH] make pallet-tips & pallet-bounties instantiable (#11473)

* make pallet-tips & pallet-bounties instantiable

* update test

* add default instance

* update

* cargo fmt

* update

* update

* update

* update

* fix merge

* fix tests

* bounties benchmarking instantiable

* fix benchmarks

* make tips benchmarks instantible

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
---
 substrate/frame/bounties/src/benchmarking.rs | 156 ++++++++-------
 substrate/frame/bounties/src/lib.rs          | 193 ++++++++++---------
 substrate/frame/bounties/src/tests.rs        |  89 +++++++--
 substrate/frame/tips/src/benchmarking.rs     |  58 +++---
 substrate/frame/tips/src/lib.rs              | 105 +++++-----
 substrate/frame/tips/src/tests.rs            |  87 ++++++++-
 6 files changed, 417 insertions(+), 271 deletions(-)

diff --git a/substrate/frame/bounties/src/benchmarking.rs b/substrate/frame/bounties/src/benchmarking.rs
index 912e461501b..7566c32f6e9 100644
--- a/substrate/frame/bounties/src/benchmarking.rs
+++ b/substrate/frame/bounties/src/benchmarking.rs
@@ -21,7 +21,7 @@
 
 use super::*;
 
-use frame_benchmarking::{account, benchmarks, whitelisted_caller};
+use frame_benchmarking::{account, benchmarks_instance_pallet, whitelisted_caller};
 use frame_system::RawOrigin;
 use sp_runtime::traits::Bounded;
 
@@ -31,25 +31,25 @@ use pallet_treasury::Pallet as Treasury;
 const SEED: u32 = 0;
 
 // Create bounties that are approved for use in `on_initialize`.
-fn create_approved_bounties<T: Config>(n: u32) -> Result<(), &'static str> {
+fn create_approved_bounties<T: Config<I>, I: 'static>(n: u32) -> Result<(), &'static str> {
 	for i in 0..n {
 		let (caller, _curator, _fee, value, reason) =
-			setup_bounty::<T>(i, T::MaximumReasonLength::get());
-		Bounties::<T>::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?;
-		let bounty_id = BountyCount::<T>::get() - 1;
-		Bounties::<T>::approve_bounty(RawOrigin::Root.into(), bounty_id)?;
+			setup_bounty::<T, I>(i, T::MaximumReasonLength::get());
+		Bounties::<T, I>::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?;
+		let bounty_id = BountyCount::<T, I>::get() - 1;
+		Bounties::<T, I>::approve_bounty(RawOrigin::Root.into(), bounty_id)?;
 	}
-	ensure!(BountyApprovals::<T>::get().len() == n as usize, "Not all bounty approved");
+	ensure!(BountyApprovals::<T, I>::get().len() == n as usize, "Not all bounty approved");
 	Ok(())
 }
 
 // Create the pre-requisite information needed to create a treasury `propose_bounty`.
-fn setup_bounty<T: Config>(
+fn setup_bounty<T: Config<I>, I: 'static>(
 	u: u32,
 	d: u32,
-) -> (T::AccountId, T::AccountId, BalanceOf<T>, BalanceOf<T>, Vec<u8>) {
+) -> (T::AccountId, T::AccountId, BalanceOf<T, I>, BalanceOf<T, I>, Vec<u8>) {
 	let caller = account("caller", u, SEED);
-	let value: BalanceOf<T> = T::BountyValueMinimum::get().saturating_mul(100u32.into());
+	let value: BalanceOf<T, I> = T::BountyValueMinimum::get().saturating_mul(100u32.into());
 	let fee = value / 2u32.into();
 	let deposit = T::BountyDepositBase::get() +
 		T::DataDepositPerByte::get() * T::MaximumReasonLength::get().into();
@@ -60,97 +60,103 @@ fn setup_bounty<T: Config>(
 	(caller, curator, fee, value, reason)
 }
 
-fn create_bounty<T: Config>(
+fn create_bounty<T: Config<I>, I: 'static>(
 ) -> Result<(<T::Lookup as StaticLookup>::Source, BountyIndex), &'static str> {
-	let (caller, curator, fee, value, reason) = setup_bounty::<T>(0, T::MaximumReasonLength::get());
+	let (caller, curator, fee, value, reason) =
+		setup_bounty::<T, I>(0, T::MaximumReasonLength::get());
 	let curator_lookup = T::Lookup::unlookup(curator.clone());
-	Bounties::<T>::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?;
-	let bounty_id = BountyCount::<T>::get() - 1;
-	Bounties::<T>::approve_bounty(RawOrigin::Root.into(), bounty_id)?;
-	Treasury::<T>::on_initialize(T::BlockNumber::zero());
-	Bounties::<T>::propose_curator(RawOrigin::Root.into(), bounty_id, curator_lookup.clone(), fee)?;
-	Bounties::<T>::accept_curator(RawOrigin::Signed(curator).into(), bounty_id)?;
+	Bounties::<T, I>::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?;
+	let bounty_id = BountyCount::<T, I>::get() - 1;
+	Bounties::<T, I>::approve_bounty(RawOrigin::Root.into(), bounty_id)?;
+	Treasury::<T, I>::on_initialize(T::BlockNumber::zero());
+	Bounties::<T, I>::propose_curator(
+		RawOrigin::Root.into(),
+		bounty_id,
+		curator_lookup.clone(),
+		fee,
+	)?;
+	Bounties::<T, I>::accept_curator(RawOrigin::Signed(curator).into(), bounty_id)?;
 	Ok((curator_lookup, bounty_id))
 }
 
-fn setup_pot_account<T: Config>() {
-	let pot_account = Bounties::<T>::account_id();
+fn setup_pot_account<T: Config<I>, I: 'static>() {
+	let pot_account = Bounties::<T, I>::account_id();
 	let value = T::Currency::minimum_balance().saturating_mul(1_000_000_000u32.into());
 	let _ = T::Currency::make_free_balance_be(&pot_account, value);
 }
 
-fn assert_last_event<T: Config>(generic_event: <T as Config>::Event) {
+fn assert_last_event<T: Config<I>, I: 'static>(generic_event: <T as Config<I>>::Event) {
 	frame_system::Pallet::<T>::assert_last_event(generic_event.into());
 }
 
-benchmarks! {
+benchmarks_instance_pallet! {
 	propose_bounty {
 		let d in 0 .. T::MaximumReasonLength::get();
 
-		let (caller, curator, fee, value, description) = setup_bounty::<T>(0, d);
+		let (caller, curator, fee, value, description) = setup_bounty::<T, I>(0, d);
 	}: _(RawOrigin::Signed(caller), value, description)
 
 	approve_bounty {
-		let (caller, curator, fee, value, reason) = setup_bounty::<T>(0, T::MaximumReasonLength::get());
-		Bounties::<T>::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?;
-		let bounty_id = BountyCount::<T>::get() - 1;
+		let (caller, curator, fee, value, reason) = setup_bounty::<T, I>(0, T::MaximumReasonLength::get());
+		Bounties::<T, I>::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?;
+		let bounty_id = BountyCount::<T, I>::get() - 1;
 	}: _(RawOrigin::Root, bounty_id)
 
 	propose_curator {
-		setup_pot_account::<T>();
-		let (caller, curator, fee, value, reason) = setup_bounty::<T>(0, T::MaximumReasonLength::get());
+		setup_pot_account::<T, I>();
+		let (caller, curator, fee, value, reason) = setup_bounty::<T, I>(0, T::MaximumReasonLength::get());
 		let curator_lookup = T::Lookup::unlookup(curator);
-		Bounties::<T>::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?;
-		let bounty_id = BountyCount::<T>::get() - 1;
-		Bounties::<T>::approve_bounty(RawOrigin::Root.into(), bounty_id)?;
-		Bounties::<T>::on_initialize(T::BlockNumber::zero());
+		Bounties::<T, I>::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?;
+		let bounty_id = BountyCount::<T, I>::get() - 1;
+		Bounties::<T, I>::approve_bounty(RawOrigin::Root.into(), bounty_id)?;
+		Treasury::<T, I>::on_initialize(T::BlockNumber::zero());
 	}: _(RawOrigin::Root, bounty_id, curator_lookup, fee)
 
 	// Worst case when curator is inactive and any sender unassigns the curator.
 	unassign_curator {
-		setup_pot_account::<T>();
-		let (curator_lookup, bounty_id) = create_bounty::<T>()?;
-		Bounties::<T>::on_initialize(T::BlockNumber::zero());
-		let bounty_id = BountyCount::<T>::get() - 1;
-		frame_system::Pallet::<T>::set_block_number(T::BountyUpdatePeriod::get() + 1u32.into());
+		setup_pot_account::<T, I>();
+		let (curator_lookup, bounty_id) = create_bounty::<T, I>()?;
+		Treasury::<T, I>::on_initialize(T::BlockNumber::zero());
+		let bounty_id = BountyCount::<T, I>::get() - 1;
+		frame_system::Pallet::<T>::set_block_number(T::BountyUpdatePeriod::get() + 2u32.into());
 		let caller = whitelisted_caller();
 	}: _(RawOrigin::Signed(caller), bounty_id)
 
 	accept_curator {
-		setup_pot_account::<T>();
-		let (caller, curator, fee, value, reason) = setup_bounty::<T>(0, T::MaximumReasonLength::get());
+		setup_pot_account::<T, I>();
+		let (caller, curator, fee, value, reason) = setup_bounty::<T, I>(0, T::MaximumReasonLength::get());
 		let curator_lookup = T::Lookup::unlookup(curator.clone());
-		Bounties::<T>::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?;
-		let bounty_id = BountyCount::<T>::get() - 1;
-		Bounties::<T>::approve_bounty(RawOrigin::Root.into(), bounty_id)?;
-		Bounties::<T>::on_initialize(T::BlockNumber::zero());
-		Bounties::<T>::propose_curator(RawOrigin::Root.into(), bounty_id, curator_lookup, fee)?;
+		Bounties::<T, I>::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?;
+		let bounty_id = BountyCount::<T, I>::get() - 1;
+		Bounties::<T, I>::approve_bounty(RawOrigin::Root.into(), bounty_id)?;
+		Treasury::<T, I>::on_initialize(T::BlockNumber::zero());
+		Bounties::<T, I>::propose_curator(RawOrigin::Root.into(), bounty_id, curator_lookup, fee)?;
 	}: _(RawOrigin::Signed(curator), bounty_id)
 
 	award_bounty {
-		setup_pot_account::<T>();
-		let (curator_lookup, bounty_id) = create_bounty::<T>()?;
-		Bounties::<T>::on_initialize(T::BlockNumber::zero());
+		setup_pot_account::<T, I>();
+		let (curator_lookup, bounty_id) = create_bounty::<T, I>()?;
+		Treasury::<T, I>::on_initialize(T::BlockNumber::zero());
 
-		let bounty_id = BountyCount::<T>::get() - 1;
+		let bounty_id = BountyCount::<T, I>::get() - 1;
 		let curator = T::Lookup::lookup(curator_lookup).map_err(<&str>::from)?;
 
 		let beneficiary = T::Lookup::unlookup(account("beneficiary", 0, SEED));
 	}: _(RawOrigin::Signed(curator), bounty_id, beneficiary)
 
 	claim_bounty {
-		setup_pot_account::<T>();
-		let (curator_lookup, bounty_id) = create_bounty::<T>()?;
-		Bounties::<T>::on_initialize(T::BlockNumber::zero());
+		setup_pot_account::<T, I>();
+		let (curator_lookup, bounty_id) = create_bounty::<T, I>()?;
+		Treasury::<T, I>::on_initialize(T::BlockNumber::zero());
 
-		let bounty_id = BountyCount::<T>::get() - 1;
+		let bounty_id = BountyCount::<T, I>::get() - 1;
 		let curator = T::Lookup::lookup(curator_lookup).map_err(<&str>::from)?;
 
 		let beneficiary_account: T::AccountId = account("beneficiary", 0, SEED);
 		let beneficiary = T::Lookup::unlookup(beneficiary_account.clone());
-		Bounties::<T>::award_bounty(RawOrigin::Signed(curator.clone()).into(), bounty_id, beneficiary)?;
+		Bounties::<T, I>::award_bounty(RawOrigin::Signed(curator.clone()).into(), bounty_id, beneficiary)?;
 
-		frame_system::Pallet::<T>::set_block_number(T::BountyDepositPayoutDelay::get());
+		frame_system::Pallet::<T>::set_block_number(T::BountyDepositPayoutDelay::get() + 1u32.into());
 		ensure!(T::Currency::free_balance(&beneficiary_account).is_zero(), "Beneficiary already has balance");
 
 	}: _(RawOrigin::Signed(curator), bounty_id)
@@ -159,45 +165,45 @@ benchmarks! {
 	}
 
 	close_bounty_proposed {
-		setup_pot_account::<T>();
-		let (caller, curator, fee, value, reason) = setup_bounty::<T>(0, 0);
-		Bounties::<T>::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?;
-		let bounty_id = BountyCount::<T>::get() - 1;
+		setup_pot_account::<T, I>();
+		let (caller, curator, fee, value, reason) = setup_bounty::<T, I>(0, 0);
+		Bounties::<T, I>::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?;
+		let bounty_id = BountyCount::<T, I>::get() - 1;
 	}: close_bounty(RawOrigin::Root, bounty_id)
 
 	close_bounty_active {
-		setup_pot_account::<T>();
-		let (curator_lookup, bounty_id) = create_bounty::<T>()?;
-		Bounties::<T>::on_initialize(T::BlockNumber::zero());
-		let bounty_id = BountyCount::<T>::get() - 1;
+		setup_pot_account::<T, I>();
+		let (curator_lookup, bounty_id) = create_bounty::<T, I>()?;
+		Treasury::<T, I>::on_initialize(T::BlockNumber::zero());
+		let bounty_id = BountyCount::<T, I>::get() - 1;
 	}: close_bounty(RawOrigin::Root, bounty_id)
 	verify {
-		assert_last_event::<T>(Event::BountyCanceled { index: bounty_id }.into())
+		assert_last_event::<T, I>(Event::BountyCanceled { index: bounty_id }.into())
 	}
 
 	extend_bounty_expiry {
-		setup_pot_account::<T>();
-		let (curator_lookup, bounty_id) = create_bounty::<T>()?;
-		Bounties::<T>::on_initialize(T::BlockNumber::zero());
+		setup_pot_account::<T, I>();
+		let (curator_lookup, bounty_id) = create_bounty::<T, I>()?;
+		Treasury::<T, I>::on_initialize(T::BlockNumber::zero());
 
-		let bounty_id = BountyCount::<T>::get() - 1;
+		let bounty_id = BountyCount::<T, I>::get() - 1;
 		let curator = T::Lookup::lookup(curator_lookup).map_err(<&str>::from)?;
 	}: _(RawOrigin::Signed(curator), bounty_id, Vec::new())
 	verify {
-		assert_last_event::<T>(Event::BountyExtended { index: bounty_id }.into())
+		assert_last_event::<T, I>(Event::BountyExtended { index: bounty_id }.into())
 	}
 
 	spend_funds {
 		let b in 1 .. 100;
-		setup_pot_account::<T>();
-		create_approved_bounties::<T>(b)?;
+		setup_pot_account::<T, I>();
+		create_approved_bounties::<T, I>(b)?;
 
-		let mut budget_remaining = BalanceOf::<T>::max_value();
-		let mut imbalance = PositiveImbalanceOf::<T>::zero();
+		let mut budget_remaining = BalanceOf::<T, I>::max_value();
+		let mut imbalance = PositiveImbalanceOf::<T, I>::zero();
 		let mut total_weight = Weight::zero();
 		let mut missed_any = false;
 	}: {
-		<Bounties<T> as pallet_treasury::SpendFunds<T>>::spend_funds(
+		<Bounties<T, I> as pallet_treasury::SpendFunds<T, I>>::spend_funds(
 			&mut budget_remaining,
 			&mut imbalance,
 			&mut total_weight,
@@ -205,9 +211,9 @@ benchmarks! {
 		);
 	}
 	verify {
-		ensure!(budget_remaining < BalanceOf::<T>::max_value(), "Budget not used");
+		ensure!(budget_remaining < BalanceOf::<T, I>::max_value(), "Budget not used");
 		ensure!(missed_any == false, "Missed some");
-		assert_last_event::<T>(Event::BountyBecameActive { index: b - 1 }.into())
+		assert_last_event::<T, I>(Event::BountyBecameActive { index: b - 1 }.into())
 	}
 
 	impl_benchmark_test_suite!(Bounties, crate::tests::new_test_ext(), crate::tests::Test)
diff --git a/substrate/frame/bounties/src/lib.rs b/substrate/frame/bounties/src/lib.rs
index b01a36b430a..8e28cacb9d9 100644
--- a/substrate/frame/bounties/src/lib.rs
+++ b/substrate/frame/bounties/src/lib.rs
@@ -107,9 +107,9 @@ pub use weights::WeightInfo;
 
 pub use pallet::*;
 
-type BalanceOf<T> = pallet_treasury::BalanceOf<T>;
+type BalanceOf<T, I = ()> = pallet_treasury::BalanceOf<T, I>;
 
-type PositiveImbalanceOf<T> = pallet_treasury::PositiveImbalanceOf<T>;
+type PositiveImbalanceOf<T, I = ()> = pallet_treasury::PositiveImbalanceOf<T, I>;
 
 /// An index of a bounty. Just a `u32`.
 pub type BountyIndex = u32;
@@ -188,13 +188,13 @@ pub mod pallet {
 
 	#[pallet::pallet]
 	#[pallet::generate_store(pub(super) trait Store)]
-	pub struct Pallet<T>(_);
+	pub struct Pallet<T, I = ()>(_);
 
 	#[pallet::config]
-	pub trait Config: frame_system::Config + pallet_treasury::Config {
+	pub trait Config<I: 'static = ()>: frame_system::Config + pallet_treasury::Config<I> {
 		/// The amount held on deposit for placing a bounty proposal.
 		#[pallet::constant]
-		type BountyDepositBase: Get<BalanceOf<Self>>;
+		type BountyDepositBase: Get<BalanceOf<Self, I>>;
 
 		/// The delay period for which a bounty beneficiary need to wait before claim the payout.
 		#[pallet::constant]
@@ -213,22 +213,22 @@ pub mod pallet {
 
 		/// Maximum amount of funds that should be placed in a deposit for making a proposal.
 		#[pallet::constant]
-		type CuratorDepositMax: Get<Option<BalanceOf<Self>>>;
+		type CuratorDepositMax: Get<Option<BalanceOf<Self, I>>>;
 
 		/// Minimum amount of funds that should be placed in a deposit for making a proposal.
 		#[pallet::constant]
-		type CuratorDepositMin: Get<Option<BalanceOf<Self>>>;
+		type CuratorDepositMin: Get<Option<BalanceOf<Self, I>>>;
 
 		/// Minimum value for a bounty.
 		#[pallet::constant]
-		type BountyValueMinimum: Get<BalanceOf<Self>>;
+		type BountyValueMinimum: Get<BalanceOf<Self, I>>;
 
 		/// The amount held on deposit per byte within the tip report reason or bounty description.
 		#[pallet::constant]
-		type DataDepositPerByte: Get<BalanceOf<Self>>;
+		type DataDepositPerByte: Get<BalanceOf<Self, I>>;
 
 		/// The overarching event type.
-		type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
+		type Event: From<Event<Self, I>> + IsType<<Self as frame_system::Config>::Event>;
 
 		/// Maximum acceptable reason length.
 		///
@@ -240,11 +240,11 @@ pub mod pallet {
 		type WeightInfo: WeightInfo;
 
 		/// The child bounty manager.
-		type ChildBountyManager: ChildBountyManager<BalanceOf<Self>>;
+		type ChildBountyManager: ChildBountyManager<BalanceOf<Self, I>>;
 	}
 
 	#[pallet::error]
-	pub enum Error<T> {
+	pub enum Error<T, I = ()> {
 		/// Proposer's balance is too low.
 		InsufficientProposersBalance,
 		/// No proposal or bounty at that index.
@@ -272,17 +272,17 @@ pub mod pallet {
 
 	#[pallet::event]
 	#[pallet::generate_deposit(pub(super) fn deposit_event)]
-	pub enum Event<T: Config> {
+	pub enum Event<T: Config<I>, I: 'static = ()> {
 		/// New bounty proposal.
 		BountyProposed { index: BountyIndex },
 		/// A bounty proposal was rejected; funds were slashed.
-		BountyRejected { index: BountyIndex, bond: BalanceOf<T> },
+		BountyRejected { index: BountyIndex, bond: BalanceOf<T, I> },
 		/// A bounty proposal is funded and became active.
 		BountyBecameActive { index: BountyIndex },
 		/// A bounty is awarded to a beneficiary.
 		BountyAwarded { index: BountyIndex, beneficiary: T::AccountId },
 		/// A bounty is claimed by beneficiary.
-		BountyClaimed { index: BountyIndex, payout: BalanceOf<T>, beneficiary: T::AccountId },
+		BountyClaimed { index: BountyIndex, payout: BalanceOf<T, I>, beneficiary: T::AccountId },
 		/// A bounty is cancelled.
 		BountyCanceled { index: BountyIndex },
 		/// A bounty expiry is extended.
@@ -292,32 +292,32 @@ pub mod pallet {
 	/// Number of bounty proposals that have been made.
 	#[pallet::storage]
 	#[pallet::getter(fn bounty_count)]
-	pub type BountyCount<T: Config> = StorageValue<_, BountyIndex, ValueQuery>;
+	pub type BountyCount<T: Config<I>, I: 'static = ()> = StorageValue<_, BountyIndex, ValueQuery>;
 
 	/// Bounties that have been made.
 	#[pallet::storage]
 	#[pallet::getter(fn bounties)]
-	pub type Bounties<T: Config> = StorageMap<
+	pub type Bounties<T: Config<I>, I: 'static = ()> = StorageMap<
 		_,
 		Twox64Concat,
 		BountyIndex,
-		Bounty<T::AccountId, BalanceOf<T>, T::BlockNumber>,
+		Bounty<T::AccountId, BalanceOf<T, I>, T::BlockNumber>,
 	>;
 
 	/// The description of each bounty.
 	#[pallet::storage]
 	#[pallet::getter(fn bounty_descriptions)]
-	pub type BountyDescriptions<T: Config> =
+	pub type BountyDescriptions<T: Config<I>, I: 'static = ()> =
 		StorageMap<_, Twox64Concat, BountyIndex, BoundedVec<u8, T::MaximumReasonLength>>;
 
 	/// Bounty indices that have been approved but not yet funded.
 	#[pallet::storage]
 	#[pallet::getter(fn bounty_approvals)]
-	pub type BountyApprovals<T: Config> =
+	pub type BountyApprovals<T: Config<I>, I: 'static = ()> =
 		StorageValue<_, BoundedVec<BountyIndex, T::MaxApprovals>, ValueQuery>;
 
 	#[pallet::call]
-	impl<T: Config> Pallet<T> {
+	impl<T: Config<I>, I: 'static> Pallet<T, I> {
 		/// Propose a new bounty.
 		///
 		/// The dispatch origin for this call must be _Signed_.
@@ -330,10 +330,10 @@ pub mod pallet {
 		/// - `fee`: The curator fee.
 		/// - `value`: The total payment amount of this bounty, curator fee included.
 		/// - `description`: The description of this bounty.
-		#[pallet::weight(<T as Config>::WeightInfo::propose_bounty(description.len() as u32))]
+		#[pallet::weight(<T as Config<I>>::WeightInfo::propose_bounty(description.len() as u32))]
 		pub fn propose_bounty(
 			origin: OriginFor<T>,
-			#[pallet::compact] value: BalanceOf<T>,
+			#[pallet::compact] value: BalanceOf<T, I>,
 			description: Vec<u8>,
 		) -> DispatchResult {
 			let proposer = ensure_signed(origin)?;
@@ -349,21 +349,21 @@ pub mod pallet {
 		/// # <weight>
 		/// - O(1).
 		/// # </weight>
-		#[pallet::weight(<T as Config>::WeightInfo::approve_bounty())]
+		#[pallet::weight(<T as Config<I>>::WeightInfo::approve_bounty())]
 		pub fn approve_bounty(
 			origin: OriginFor<T>,
 			#[pallet::compact] bounty_id: BountyIndex,
 		) -> DispatchResult {
 			T::ApproveOrigin::ensure_origin(origin)?;
 
-			Bounties::<T>::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult {
-				let mut bounty = maybe_bounty.as_mut().ok_or(Error::<T>::InvalidIndex)?;
-				ensure!(bounty.status == BountyStatus::Proposed, Error::<T>::UnexpectedStatus);
+			Bounties::<T, I>::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult {
+				let mut bounty = maybe_bounty.as_mut().ok_or(Error::<T, I>::InvalidIndex)?;
+				ensure!(bounty.status == BountyStatus::Proposed, Error::<T, I>::UnexpectedStatus);
 
 				bounty.status = BountyStatus::Approved;
 
-				BountyApprovals::<T>::try_append(bounty_id)
-					.map_err(|()| Error::<T>::TooManyQueued)?;
+				BountyApprovals::<T, I>::try_append(bounty_id)
+					.map_err(|()| Error::<T, I>::TooManyQueued)?;
 
 				Ok(())
 			})?;
@@ -377,24 +377,24 @@ pub mod pallet {
 		/// # <weight>
 		/// - O(1).
 		/// # </weight>
-		#[pallet::weight(<T as Config>::WeightInfo::propose_curator())]
+		#[pallet::weight(<T as Config<I>>::WeightInfo::propose_curator())]
 		pub fn propose_curator(
 			origin: OriginFor<T>,
 			#[pallet::compact] bounty_id: BountyIndex,
 			curator: <T::Lookup as StaticLookup>::Source,
-			#[pallet::compact] fee: BalanceOf<T>,
+			#[pallet::compact] fee: BalanceOf<T, I>,
 		) -> DispatchResult {
 			T::ApproveOrigin::ensure_origin(origin)?;
 
 			let curator = T::Lookup::lookup(curator)?;
-			Bounties::<T>::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult {
-				let mut bounty = maybe_bounty.as_mut().ok_or(Error::<T>::InvalidIndex)?;
+			Bounties::<T, I>::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult {
+				let mut bounty = maybe_bounty.as_mut().ok_or(Error::<T, I>::InvalidIndex)?;
 				match bounty.status {
 					BountyStatus::Proposed | BountyStatus::Approved | BountyStatus::Funded => {},
-					_ => return Err(Error::<T>::UnexpectedStatus.into()),
+					_ => return Err(Error::<T, I>::UnexpectedStatus.into()),
 				};
 
-				ensure!(fee < bounty.value, Error::<T>::InvalidFee);
+				ensure!(fee < bounty.value, Error::<T, I>::InvalidFee);
 
 				bounty.status = BountyStatus::CuratorProposed { curator };
 				bounty.fee = fee;
@@ -422,7 +422,7 @@ pub mod pallet {
 		/// # <weight>
 		/// - O(1).
 		/// # </weight>
-		#[pallet::weight(<T as Config>::WeightInfo::unassign_curator())]
+		#[pallet::weight(<T as Config<I>>::WeightInfo::unassign_curator())]
 		pub fn unassign_curator(
 			origin: OriginFor<T>,
 			#[pallet::compact] bounty_id: BountyIndex,
@@ -431,10 +431,11 @@ pub mod pallet {
 				.map(Some)
 				.or_else(|_| T::RejectOrigin::ensure_origin(origin).map(|_| None))?;
 
-			Bounties::<T>::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult {
-				let mut bounty = maybe_bounty.as_mut().ok_or(Error::<T>::InvalidIndex)?;
+			Bounties::<T, I>::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult {
+				let mut bounty = maybe_bounty.as_mut().ok_or(Error::<T, I>::InvalidIndex)?;
 
-				let slash_curator = |curator: &T::AccountId, curator_deposit: &mut BalanceOf<T>| {
+				let slash_curator = |curator: &T::AccountId,
+				                     curator_deposit: &mut BalanceOf<T, I>| {
 					let imbalance = T::Currency::slash_reserved(curator, *curator_deposit).0;
 					T::OnSlash::on_unbalanced(imbalance);
 					*curator_deposit = Zero::zero();
@@ -443,7 +444,7 @@ pub mod pallet {
 				match bounty.status {
 					BountyStatus::Proposed | BountyStatus::Approved | BountyStatus::Funded => {
 						// No curator to unassign at this point.
-						return Err(Error::<T>::UnexpectedStatus.into())
+						return Err(Error::<T, I>::UnexpectedStatus.into())
 					},
 					BountyStatus::CuratorProposed { ref curator } => {
 						// A curator has been proposed, but not accepted yet.
@@ -468,7 +469,7 @@ pub mod pallet {
 									// Continue to change bounty status below...
 									} else {
 										// Curator has more time to give an update.
-										return Err(Error::<T>::Premature.into())
+										return Err(Error::<T, I>::Premature.into())
 									}
 								} else {
 									// Else this is the curator, willingly giving up their role.
@@ -506,19 +507,19 @@ pub mod pallet {
 		/// # <weight>
 		/// - O(1).
 		/// # </weight>
-		#[pallet::weight(<T as Config>::WeightInfo::accept_curator())]
+		#[pallet::weight(<T as Config<I>>::WeightInfo::accept_curator())]
 		pub fn accept_curator(
 			origin: OriginFor<T>,
 			#[pallet::compact] bounty_id: BountyIndex,
 		) -> DispatchResult {
 			let signer = ensure_signed(origin)?;
 
-			Bounties::<T>::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult {
-				let mut bounty = maybe_bounty.as_mut().ok_or(Error::<T>::InvalidIndex)?;
+			Bounties::<T, I>::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult {
+				let mut bounty = maybe_bounty.as_mut().ok_or(Error::<T, I>::InvalidIndex)?;
 
 				match bounty.status {
 					BountyStatus::CuratorProposed { ref curator } => {
-						ensure!(signer == *curator, Error::<T>::RequireCurator);
+						ensure!(signer == *curator, Error::<T, I>::RequireCurator);
 
 						let deposit = Self::calculate_curator_deposit(&bounty.fee);
 						T::Currency::reserve(curator, deposit)?;
@@ -531,7 +532,7 @@ pub mod pallet {
 
 						Ok(())
 					},
-					_ => Err(Error::<T>::UnexpectedStatus.into()),
+					_ => Err(Error::<T, I>::UnexpectedStatus.into()),
 				}
 			})?;
 			Ok(())
@@ -548,7 +549,7 @@ pub mod pallet {
 		/// # <weight>
 		/// - O(1).
 		/// # </weight>
-		#[pallet::weight(<T as Config>::WeightInfo::award_bounty())]
+		#[pallet::weight(<T as Config<I>>::WeightInfo::award_bounty())]
 		pub fn award_bounty(
 			origin: OriginFor<T>,
 			#[pallet::compact] bounty_id: BountyIndex,
@@ -557,20 +558,20 @@ pub mod pallet {
 			let signer = ensure_signed(origin)?;
 			let beneficiary = T::Lookup::lookup(beneficiary)?;
 
-			Bounties::<T>::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult {
-				let mut bounty = maybe_bounty.as_mut().ok_or(Error::<T>::InvalidIndex)?;
+			Bounties::<T, I>::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult {
+				let mut bounty = maybe_bounty.as_mut().ok_or(Error::<T, I>::InvalidIndex)?;
 
 				// Ensure no active child bounties before processing the call.
 				ensure!(
 					T::ChildBountyManager::child_bounties_count(bounty_id) == 0,
-					Error::<T>::HasActiveChildBounty
+					Error::<T, I>::HasActiveChildBounty
 				);
 
 				match &bounty.status {
 					BountyStatus::Active { curator, .. } => {
-						ensure!(signer == *curator, Error::<T>::RequireCurator);
+						ensure!(signer == *curator, Error::<T, I>::RequireCurator);
 					},
-					_ => return Err(Error::<T>::UnexpectedStatus.into()),
+					_ => return Err(Error::<T, I>::UnexpectedStatus.into()),
 				}
 				bounty.status = BountyStatus::PendingPayout {
 					curator: signer,
@@ -582,7 +583,7 @@ pub mod pallet {
 				Ok(())
 			})?;
 
-			Self::deposit_event(Event::<T>::BountyAwarded { index: bounty_id, beneficiary });
+			Self::deposit_event(Event::<T, I>::BountyAwarded { index: bounty_id, beneficiary });
 			Ok(())
 		}
 
@@ -595,21 +596,21 @@ pub mod pallet {
 		/// # <weight>
 		/// - O(1).
 		/// # </weight>
-		#[pallet::weight(<T as Config>::WeightInfo::claim_bounty())]
+		#[pallet::weight(<T as Config<I>>::WeightInfo::claim_bounty())]
 		pub fn claim_bounty(
 			origin: OriginFor<T>,
 			#[pallet::compact] bounty_id: BountyIndex,
 		) -> DispatchResult {
 			let _ = ensure_signed(origin)?; // anyone can trigger claim
 
-			Bounties::<T>::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult {
-				let bounty = maybe_bounty.take().ok_or(Error::<T>::InvalidIndex)?;
+			Bounties::<T, I>::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult {
+				let bounty = maybe_bounty.take().ok_or(Error::<T, I>::InvalidIndex)?;
 				if let BountyStatus::PendingPayout { curator, beneficiary, unlock_at } =
 					bounty.status
 				{
 					ensure!(
 						frame_system::Pallet::<T>::block_number() >= unlock_at,
-						Error::<T>::Premature
+						Error::<T, I>::Premature
 					);
 					let bounty_account = Self::bounty_account_id(bounty_id);
 					let balance = T::Currency::free_balance(&bounty_account);
@@ -633,16 +634,16 @@ pub mod pallet {
 
 					*maybe_bounty = None;
 
-					BountyDescriptions::<T>::remove(bounty_id);
+					BountyDescriptions::<T, I>::remove(bounty_id);
 
-					Self::deposit_event(Event::<T>::BountyClaimed {
+					Self::deposit_event(Event::<T, I>::BountyClaimed {
 						index: bounty_id,
 						payout,
 						beneficiary,
 					});
 					Ok(())
 				} else {
-					Err(Error::<T>::UnexpectedStatus.into())
+					Err(Error::<T, I>::UnexpectedStatus.into())
 				}
 			})?;
 			Ok(())
@@ -658,47 +659,47 @@ pub mod pallet {
 		/// # <weight>
 		/// - O(1).
 		/// # </weight>
-		#[pallet::weight(<T as Config>::WeightInfo::close_bounty_proposed()
-			.max(<T as Config>::WeightInfo::close_bounty_active()))]
+		#[pallet::weight(<T as Config<I>>::WeightInfo::close_bounty_proposed()
+			.max(<T as Config<I>>::WeightInfo::close_bounty_active()))]
 		pub fn close_bounty(
 			origin: OriginFor<T>,
 			#[pallet::compact] bounty_id: BountyIndex,
 		) -> DispatchResultWithPostInfo {
 			T::RejectOrigin::ensure_origin(origin)?;
 
-			Bounties::<T>::try_mutate_exists(
+			Bounties::<T, I>::try_mutate_exists(
 				bounty_id,
 				|maybe_bounty| -> DispatchResultWithPostInfo {
-					let bounty = maybe_bounty.as_ref().ok_or(Error::<T>::InvalidIndex)?;
+					let bounty = maybe_bounty.as_ref().ok_or(Error::<T, I>::InvalidIndex)?;
 
 					// Ensure no active child bounties before processing the call.
 					ensure!(
 						T::ChildBountyManager::child_bounties_count(bounty_id) == 0,
-						Error::<T>::HasActiveChildBounty
+						Error::<T, I>::HasActiveChildBounty
 					);
 
 					match &bounty.status {
 						BountyStatus::Proposed => {
 							// The reject origin would like to cancel a proposed bounty.
-							BountyDescriptions::<T>::remove(bounty_id);
+							BountyDescriptions::<T, I>::remove(bounty_id);
 							let value = bounty.bond;
 							let imbalance = T::Currency::slash_reserved(&bounty.proposer, value).0;
 							T::OnSlash::on_unbalanced(imbalance);
 							*maybe_bounty = None;
 
-							Self::deposit_event(Event::<T>::BountyRejected {
+							Self::deposit_event(Event::<T, I>::BountyRejected {
 								index: bounty_id,
 								bond: value,
 							});
 							// Return early, nothing else to do.
 							return Ok(
-								Some(<T as Config>::WeightInfo::close_bounty_proposed()).into()
+								Some(<T as Config<I>>::WeightInfo::close_bounty_proposed()).into()
 							)
 						},
 						BountyStatus::Approved => {
 							// For weight reasons, we don't allow a council to cancel in this phase.
 							// We ask for them to wait until it is funded before they can cancel.
-							return Err(Error::<T>::UnexpectedStatus.into())
+							return Err(Error::<T, I>::UnexpectedStatus.into())
 						},
 						BountyStatus::Funded | BountyStatus::CuratorProposed { .. } => {
 							// Nothing extra to do besides the removal of the bounty below.
@@ -715,13 +716,13 @@ pub mod pallet {
 							// this bounty, it should mean the curator was acting maliciously.
 							// So the council should first unassign the curator, slashing their
 							// deposit.
-							return Err(Error::<T>::PendingPayout.into())
+							return Err(Error::<T, I>::PendingPayout.into())
 						},
 					}
 
 					let bounty_account = Self::bounty_account_id(bounty_id);
 
-					BountyDescriptions::<T>::remove(bounty_id);
+					BountyDescriptions::<T, I>::remove(bounty_id);
 
 					let balance = T::Currency::free_balance(&bounty_account);
 					let res = T::Currency::transfer(
@@ -733,8 +734,8 @@ pub mod pallet {
 					debug_assert!(res.is_ok());
 					*maybe_bounty = None;
 
-					Self::deposit_event(Event::<T>::BountyCanceled { index: bounty_id });
-					Ok(Some(<T as Config>::WeightInfo::close_bounty_active()).into())
+					Self::deposit_event(Event::<T, I>::BountyCanceled { index: bounty_id });
+					Ok(Some(<T as Config<I>>::WeightInfo::close_bounty_active()).into())
 				},
 			)
 		}
@@ -749,7 +750,7 @@ pub mod pallet {
 		/// # <weight>
 		/// - O(1).
 		/// # </weight>
-		#[pallet::weight(<T as Config>::WeightInfo::extend_bounty_expiry())]
+		#[pallet::weight(<T as Config<I>>::WeightInfo::extend_bounty_expiry())]
 		pub fn extend_bounty_expiry(
 			origin: OriginFor<T>,
 			#[pallet::compact] bounty_id: BountyIndex,
@@ -757,30 +758,30 @@ pub mod pallet {
 		) -> DispatchResult {
 			let signer = ensure_signed(origin)?;
 
-			Bounties::<T>::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult {
-				let bounty = maybe_bounty.as_mut().ok_or(Error::<T>::InvalidIndex)?;
+			Bounties::<T, I>::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult {
+				let bounty = maybe_bounty.as_mut().ok_or(Error::<T, I>::InvalidIndex)?;
 
 				match bounty.status {
 					BountyStatus::Active { ref curator, ref mut update_due } => {
-						ensure!(*curator == signer, Error::<T>::RequireCurator);
+						ensure!(*curator == signer, Error::<T, I>::RequireCurator);
 						*update_due = (frame_system::Pallet::<T>::block_number() +
 							T::BountyUpdatePeriod::get())
 						.max(*update_due);
 					},
-					_ => return Err(Error::<T>::UnexpectedStatus.into()),
+					_ => return Err(Error::<T, I>::UnexpectedStatus.into()),
 				}
 
 				Ok(())
 			})?;
 
-			Self::deposit_event(Event::<T>::BountyExtended { index: bounty_id });
+			Self::deposit_event(Event::<T, I>::BountyExtended { index: bounty_id });
 			Ok(())
 		}
 	}
 }
 
-impl<T: Config> Pallet<T> {
-	pub fn calculate_curator_deposit(fee: &BalanceOf<T>) -> BalanceOf<T> {
+impl<T: Config<I>, I: 'static> Pallet<T, I> {
+	pub fn calculate_curator_deposit(fee: &BalanceOf<T, I>) -> BalanceOf<T, I> {
 		let mut deposit = T::CuratorDepositMultiplier::get() * *fee;
 
 		if let Some(max_deposit) = T::CuratorDepositMax::get() {
@@ -812,11 +813,11 @@ impl<T: Config> Pallet<T> {
 	fn create_bounty(
 		proposer: T::AccountId,
 		description: Vec<u8>,
-		value: BalanceOf<T>,
+		value: BalanceOf<T, I>,
 	) -> DispatchResult {
 		let bounded_description: BoundedVec<_, _> =
-			description.try_into().map_err(|()| Error::<T>::ReasonTooBig)?;
-		ensure!(value >= T::BountyValueMinimum::get(), Error::<T>::InvalidValue);
+			description.try_into().map_err(|()| Error::<T, I>::ReasonTooBig)?;
+		ensure!(value >= T::BountyValueMinimum::get(), Error::<T, I>::InvalidValue);
 
 		let index = Self::bounty_count();
 
@@ -824,9 +825,9 @@ impl<T: Config> Pallet<T> {
 		let bond = T::BountyDepositBase::get() +
 			T::DataDepositPerByte::get() * (bounded_description.len() as u32).into();
 		T::Currency::reserve(&proposer, bond)
-			.map_err(|_| Error::<T>::InsufficientProposersBalance)?;
+			.map_err(|_| Error::<T, I>::InsufficientProposersBalance)?;
 
-		BountyCount::<T>::put(index + 1);
+		BountyCount::<T, I>::put(index + 1);
 
 		let bounty = Bounty {
 			proposer,
@@ -837,26 +838,26 @@ impl<T: Config> Pallet<T> {
 			status: BountyStatus::Proposed,
 		};
 
-		Bounties::<T>::insert(index, &bounty);
-		BountyDescriptions::<T>::insert(index, bounded_description);
+		Bounties::<T, I>::insert(index, &bounty);
+		BountyDescriptions::<T, I>::insert(index, bounded_description);
 
-		Self::deposit_event(Event::<T>::BountyProposed { index });
+		Self::deposit_event(Event::<T, I>::BountyProposed { index });
 
 		Ok(())
 	}
 }
 
-impl<T: Config> pallet_treasury::SpendFunds<T> for Pallet<T> {
+impl<T: Config<I>, I: 'static> pallet_treasury::SpendFunds<T, I> for Pallet<T, I> {
 	fn spend_funds(
-		budget_remaining: &mut BalanceOf<T>,
-		imbalance: &mut PositiveImbalanceOf<T>,
+		budget_remaining: &mut BalanceOf<T, I>,
+		imbalance: &mut PositiveImbalanceOf<T, I>,
 		total_weight: &mut Weight,
 		missed_any: &mut bool,
 	) {
-		let bounties_len = BountyApprovals::<T>::mutate(|v| {
+		let bounties_len = BountyApprovals::<T, I>::mutate(|v| {
 			let bounties_approval_len = v.len() as u32;
 			v.retain(|&index| {
-				Bounties::<T>::mutate(index, |bounty| {
+				Bounties::<T, I>::mutate(index, |bounty| {
 					// Should always be true, but shouldn't panic if false or we're screwed.
 					if let Some(bounty) = bounty {
 						if bounty.value <= *budget_remaining {
@@ -874,7 +875,7 @@ impl<T: Config> pallet_treasury::SpendFunds<T> for Pallet<T> {
 								bounty.value,
 							));
 
-							Self::deposit_event(Event::<T>::BountyBecameActive { index });
+							Self::deposit_event(Event::<T, I>::BountyBecameActive { index });
 							false
 						} else {
 							*missed_any = true;
@@ -888,7 +889,7 @@ impl<T: Config> pallet_treasury::SpendFunds<T> for Pallet<T> {
 			bounties_approval_len
 		});
 
-		*total_weight += <T as Config>::WeightInfo::spend_funds(bounties_len);
+		*total_weight += <T as pallet::Config<I>>::WeightInfo::spend_funds(bounties_len);
 	}
 }
 
diff --git a/substrate/frame/bounties/src/tests.rs b/substrate/frame/bounties/src/tests.rs
index 0904e3a2901..ff220600794 100644
--- a/substrate/frame/bounties/src/tests.rs
+++ b/substrate/frame/bounties/src/tests.rs
@@ -35,7 +35,7 @@ use sp_core::H256;
 use sp_runtime::{
 	testing::Header,
 	traits::{BadOrigin, BlakeTwo256, IdentityLookup},
-	Perbill, Storage,
+	BuildStorage, Perbill, Storage,
 };
 
 use super::Event as BountiesEvent;
@@ -52,7 +52,9 @@ frame_support::construct_runtime!(
 		System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
 		Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
 		Bounties: pallet_bounties::{Pallet, Call, Storage, Event<T>},
+		Bounties1: pallet_bounties::<Instance1>::{Pallet, Call, Storage, Event<T>},
 		Treasury: pallet_treasury::{Pallet, Call, Storage, Config, Event<T>},
+		Treasury1: pallet_treasury::<Instance1>::{Pallet, Call, Storage, Config, Event<T>},
 	}
 );
 
@@ -105,8 +107,9 @@ thread_local! {
 }
 parameter_types! {
 	pub const ProposalBond: Permill = Permill::from_percent(5);
-	pub const Burn: Permill = Permill::from_percent(50);
+	pub static Burn: Permill = Permill::from_percent(50);
 	pub const TreasuryPalletId: PalletId = PalletId(*b"py/trsry");
+	pub const TreasuryPalletId2: PalletId = PalletId(*b"py/trsr2");
 }
 
 impl pallet_treasury::Config for Test {
@@ -128,6 +131,25 @@ impl pallet_treasury::Config for Test {
 	type SpendOrigin = frame_support::traits::NeverEnsureOrigin<u64>;
 }
 
+impl pallet_treasury::Config<Instance1> for Test {
+	type PalletId = TreasuryPalletId2;
+	type Currency = pallet_balances::Pallet<Test>;
+	type ApproveOrigin = frame_system::EnsureRoot<u128>;
+	type RejectOrigin = frame_system::EnsureRoot<u128>;
+	type Event = Event;
+	type OnSlash = ();
+	type ProposalBond = ProposalBond;
+	type ProposalBondMinimum = ConstU64<1>;
+	type ProposalBondMaximum = ();
+	type SpendPeriod = ConstU64<2>;
+	type Burn = Burn;
+	type BurnDestination = (); // Just gets burned.
+	type WeightInfo = ();
+	type SpendFunds = Bounties1;
+	type MaxApprovals = ConstU32<100>;
+	type SpendOrigin = frame_support::traits::NeverEnsureOrigin<u64>;
+}
+
 parameter_types! {
 	// This will be 50% of the bounty fee.
 	pub const CuratorDepositMultiplier: Permill = Permill::from_percent(50);
@@ -151,18 +173,35 @@ impl Config for Test {
 	type ChildBountyManager = ();
 }
 
+impl Config<Instance1> for Test {
+	type Event = Event;
+	type BountyDepositBase = ConstU64<80>;
+	type BountyDepositPayoutDelay = ConstU64<3>;
+	type BountyUpdatePeriod = ConstU64<20>;
+	type CuratorDepositMultiplier = CuratorDepositMultiplier;
+	type CuratorDepositMax = CuratorDepositMax;
+	type CuratorDepositMin = CuratorDepositMin;
+	type BountyValueMinimum = ConstU64<1>;
+	type DataDepositPerByte = ConstU64<1>;
+	type MaximumReasonLength = ConstU32<16384>;
+	type WeightInfo = ();
+	type ChildBountyManager = ();
+}
+
 type TreasuryError = pallet_treasury::Error<Test>;
 
 pub fn new_test_ext() -> sp_io::TestExternalities {
-	let mut t = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap();
-	pallet_balances::GenesisConfig::<Test> {
-		// Total issuance will be 200 with treasury account initialized at ED.
-		balances: vec![(0, 100), (1, 98), (2, 1)],
+	let mut ext: sp_io::TestExternalities = GenesisConfig {
+		system: frame_system::GenesisConfig::default(),
+		balances: pallet_balances::GenesisConfig { balances: vec![(0, 100), (1, 98), (2, 1)] },
+		treasury: Default::default(),
+		treasury_1: Default::default(),
 	}
-	.assimilate_storage(&mut t)
-	.unwrap();
-	GenesisBuild::<Test>::assimilate_storage(&pallet_treasury::GenesisConfig, &mut t).unwrap();
-	t.into()
+	.build_storage()
+	.unwrap()
+	.into();
+	ext.execute_with(|| System::set_block_number(1));
+	ext
 }
 
 fn last_event() -> BountiesEvent<Test> {
@@ -276,7 +315,7 @@ fn reject_non_existent_spend_proposal_fails() {
 	new_test_ext().execute_with(|| {
 		assert_noop!(
 			Treasury::reject_proposal(Origin::root(), 0),
-			pallet_treasury::Error::<Test, _>::InvalidIndex
+			pallet_treasury::Error::<Test>::InvalidIndex
 		);
 	});
 }
@@ -469,7 +508,7 @@ fn close_bounty_works() {
 		assert_eq!(Balances::free_balance(0), 100 - deposit);
 
 		assert_eq!(Bounties::bounties(0), None);
-		assert!(!pallet_treasury::Proposals::<Test, _>::contains_key(0));
+		assert!(!pallet_treasury::Proposals::<Test>::contains_key(0));
 
 		assert_eq!(Bounties::bounty_descriptions(0), None);
 	});
@@ -1122,3 +1161,29 @@ fn accept_curator_handles_different_deposit_calculations() {
 		assert_eq!(Balances::reserved_balance(&user), expected_deposit);
 	});
 }
+
+#[test]
+fn approve_bounty_works_second_instance() {
+	new_test_ext().execute_with(|| {
+		// Set burn to 0 to make tracking funds easier.
+		Burn::set(Permill::from_percent(0));
+
+		System::set_block_number(1);
+		Balances::make_free_balance_be(&Treasury::account_id(), 101);
+		Balances::make_free_balance_be(&Treasury1::account_id(), 201);
+		assert_eq!(Balances::free_balance(&Treasury::account_id()), 101);
+		assert_eq!(Balances::free_balance(&Treasury1::account_id()), 201);
+
+		assert_ok!(Bounties1::propose_bounty(Origin::signed(0), 50, b"12345".to_vec()));
+		assert_ok!(Bounties1::approve_bounty(Origin::root(), 0));
+		<Treasury as OnInitialize<u64>>::on_initialize(2);
+		<Treasury1 as OnInitialize<u64>>::on_initialize(2);
+
+		// Bounties 1 is funded... but from where?
+		assert_eq!(Balances::free_balance(Bounties1::bounty_account_id(0)), 50);
+		// Treasury 1 unchanged
+		assert_eq!(Balances::free_balance(&Treasury::account_id()), 101);
+		// Treasury 2 has funds removed
+		assert_eq!(Balances::free_balance(&Treasury1::account_id()), 201 - 50);
+	});
+}
diff --git a/substrate/frame/tips/src/benchmarking.rs b/substrate/frame/tips/src/benchmarking.rs
index 190ef60f3b8..33e455bd3b9 100644
--- a/substrate/frame/tips/src/benchmarking.rs
+++ b/substrate/frame/tips/src/benchmarking.rs
@@ -19,7 +19,7 @@
 
 #![cfg(feature = "runtime-benchmarks")]
 
-use frame_benchmarking::{account, benchmarks, whitelisted_caller};
+use frame_benchmarking::{account, benchmarks_instance_pallet, whitelisted_caller};
 use frame_support::ensure;
 use frame_system::RawOrigin;
 use sp_runtime::traits::Saturating;
@@ -30,7 +30,7 @@ use crate::Pallet as TipsMod;
 const SEED: u32 = 0;
 
 // Create the pre-requisite information needed to create a `report_awesome`.
-fn setup_awesome<T: Config>(length: u32) -> (T::AccountId, Vec<u8>, T::AccountId) {
+fn setup_awesome<T: Config<I>, I: 'static>(length: u32) -> (T::AccountId, Vec<u8>, T::AccountId) {
 	let caller = whitelisted_caller();
 	let value = T::TipReportDepositBase::get() +
 		T::DataDepositPerByte::get() * length.into() +
@@ -42,10 +42,10 @@ fn setup_awesome<T: Config>(length: u32) -> (T::AccountId, Vec<u8>, T::AccountId
 }
 
 // Create the pre-requisite information needed to call `tip_new`.
-fn setup_tip<T: Config>(
+fn setup_tip<T: Config<I>, I: 'static>(
 	r: u32,
 	t: u32,
-) -> Result<(T::AccountId, Vec<u8>, T::AccountId, BalanceOf<T>), &'static str> {
+) -> Result<(T::AccountId, Vec<u8>, T::AccountId, BalanceOf<T, I>), &'static str> {
 	let tippers_count = T::Tippers::count();
 
 	for i in 0..t {
@@ -64,13 +64,17 @@ fn setup_tip<T: Config>(
 
 // Create `t` new tips for the tip proposal with `hash`.
 // This function automatically makes the tip able to close.
-fn create_tips<T: Config>(t: u32, hash: T::Hash, value: BalanceOf<T>) -> Result<(), &'static str> {
+fn create_tips<T: Config<I>, I: 'static>(
+	t: u32,
+	hash: T::Hash,
+	value: BalanceOf<T, I>,
+) -> Result<(), &'static str> {
 	for i in 0..t {
 		let caller = account("member", i, SEED);
 		ensure!(T::Tippers::contains(&caller), "caller is not a tipper");
-		TipsMod::<T>::tip(RawOrigin::Signed(caller).into(), hash, value)?;
+		TipsMod::<T, I>::tip(RawOrigin::Signed(caller).into(), hash, value)?;
 	}
-	Tips::<T>::mutate(hash, |maybe_tip| {
+	Tips::<T, I>::mutate(hash, |maybe_tip| {
 		if let Some(open_tip) = maybe_tip {
 			open_tip.closes = Some(T::BlockNumber::zero());
 		}
@@ -78,16 +82,16 @@ fn create_tips<T: Config>(t: u32, hash: T::Hash, value: BalanceOf<T>) -> Result<
 	Ok(())
 }
 
-fn setup_pot_account<T: Config>() {
-	let pot_account = TipsMod::<T>::account_id();
+fn setup_pot_account<T: Config<I>, I: 'static>() {
+	let pot_account = TipsMod::<T, I>::account_id();
 	let value = T::Currency::minimum_balance().saturating_mul(1_000_000_000u32.into());
 	let _ = T::Currency::make_free_balance_be(&pot_account, value);
 }
 
-benchmarks! {
+benchmarks_instance_pallet! {
 	report_awesome {
 		let r in 0 .. T::MaximumReasonLength::get();
-		let (caller, reason, awesome_person) = setup_awesome::<T>(r);
+		let (caller, reason, awesome_person) = setup_awesome::<T, I>(r);
 		// Whitelist caller account from further DB operations.
 		let caller_key = frame_system::Account::<T>::hashed_key_for(&caller);
 		frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into());
@@ -95,8 +99,8 @@ benchmarks! {
 
 	retract_tip {
 		let r = T::MaximumReasonLength::get();
-		let (caller, reason, awesome_person) = setup_awesome::<T>(r);
-		TipsMod::<T>::report_awesome(
+		let (caller, reason, awesome_person) = setup_awesome::<T, I>(r);
+		TipsMod::<T, I>::report_awesome(
 			RawOrigin::Signed(caller.clone()).into(),
 			reason.clone(),
 			awesome_person.clone()
@@ -112,7 +116,7 @@ benchmarks! {
 		let r in 0 .. T::MaximumReasonLength::get();
 		let t in 1 .. T::Tippers::max_len() as u32;
 
-		let (caller, reason, beneficiary, value) = setup_tip::<T>(r, t)?;
+		let (caller, reason, beneficiary, value) = setup_tip::<T, I>(r, t)?;
 		// Whitelist caller account from further DB operations.
 		let caller_key = frame_system::Account::<T>::hashed_key_for(&caller);
 		frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into());
@@ -120,9 +124,9 @@ benchmarks! {
 
 	tip {
 		let t in 1 .. T::Tippers::max_len() as u32;
-		let (member, reason, beneficiary, value) = setup_tip::<T>(0, t)?;
+		let (member, reason, beneficiary, value) = setup_tip::<T, I>(0, t)?;
 		let value = T::Currency::minimum_balance().saturating_mul(100u32.into());
-		TipsMod::<T>::tip_new(
+		TipsMod::<T, I>::tip_new(
 			RawOrigin::Signed(member).into(),
 			reason.clone(),
 			beneficiary.clone(),
@@ -130,8 +134,8 @@ benchmarks! {
 		)?;
 		let reason_hash = T::Hashing::hash(&reason[..]);
 		let hash = T::Hashing::hash_of(&(&reason_hash, &beneficiary));
-		ensure!(Tips::<T>::contains_key(hash), "tip does not exist");
-		create_tips::<T>(t - 1, hash, value)?;
+		ensure!(Tips::<T, I>::contains_key(hash), "tip does not exist");
+		create_tips::<T, I>(t - 1, hash, value)?;
 		let caller = account("member", t - 1, SEED);
 		// Whitelist caller account from further DB operations.
 		let caller_key = frame_system::Account::<T>::hashed_key_for(&caller);
@@ -142,12 +146,12 @@ benchmarks! {
 		let t in 1 .. T::Tippers::max_len() as u32;
 
 		// Make sure pot is funded
-		setup_pot_account::<T>();
+		setup_pot_account::<T, I>();
 
 		// Set up a new tip proposal
-		let (member, reason, beneficiary, value) = setup_tip::<T>(0, t)?;
+		let (member, reason, beneficiary, value) = setup_tip::<T, I>(0, t)?;
 		let value = T::Currency::minimum_balance().saturating_mul(100u32.into());
-		TipsMod::<T>::tip_new(
+		TipsMod::<T, I>::tip_new(
 			RawOrigin::Signed(member).into(),
 			reason.clone(),
 			beneficiary.clone(),
@@ -157,9 +161,9 @@ benchmarks! {
 		// Create a bunch of tips
 		let reason_hash = T::Hashing::hash(&reason[..]);
 		let hash = T::Hashing::hash_of(&(&reason_hash, &beneficiary));
-		ensure!(Tips::<T>::contains_key(hash), "tip does not exist");
+		ensure!(Tips::<T, I>::contains_key(hash), "tip does not exist");
 
-		create_tips::<T>(t, hash, value)?;
+		create_tips::<T, I>(t, hash, value)?;
 
 		let caller = account("caller", t, SEED);
 		// Whitelist caller account from further DB operations.
@@ -171,12 +175,12 @@ benchmarks! {
 		let t in 1 .. T::Tippers::max_len() as u32;
 
 		// Make sure pot is funded
-		setup_pot_account::<T>();
+		setup_pot_account::<T, I>();
 
 		// Set up a new tip proposal
-		let (member, reason, beneficiary, value) = setup_tip::<T>(0, t)?;
+		let (member, reason, beneficiary, value) = setup_tip::<T, I>(0, t)?;
 		let value = T::Currency::minimum_balance().saturating_mul(100u32.into());
-		TipsMod::<T>::tip_new(
+		TipsMod::<T, I>::tip_new(
 			RawOrigin::Signed(member).into(),
 			reason.clone(),
 			beneficiary.clone(),
@@ -185,7 +189,7 @@ benchmarks! {
 
 		let reason_hash = T::Hashing::hash(&reason[..]);
 		let hash = T::Hashing::hash_of(&(&reason_hash, &beneficiary));
-		ensure!(Tips::<T>::contains_key(hash), "tip does not exist");
+		ensure!(Tips::<T, I>::contains_key(hash), "tip does not exist");
 	}: _(RawOrigin::Root, hash)
 
 	impl_benchmark_test_suite!(TipsMod, crate::tests::new_test_ext(), crate::tests::Test);
diff --git a/substrate/frame/tips/src/lib.rs b/substrate/frame/tips/src/lib.rs
index e1c7b5e77c0..71af87b42b5 100644
--- a/substrate/frame/tips/src/lib.rs
+++ b/substrate/frame/tips/src/lib.rs
@@ -78,8 +78,8 @@ use frame_support::{
 pub use pallet::*;
 pub use weights::WeightInfo;
 
-pub type BalanceOf<T> = pallet_treasury::BalanceOf<T>;
-pub type NegativeImbalanceOf<T> = pallet_treasury::NegativeImbalanceOf<T>;
+pub type BalanceOf<T, I = ()> = pallet_treasury::BalanceOf<T, I>;
+pub type NegativeImbalanceOf<T, I = ()> = pallet_treasury::NegativeImbalanceOf<T, I>;
 
 /// An open tipping "motion". Retains all details of a tip including information on the finder
 /// and the members who have voted.
@@ -121,12 +121,12 @@ pub mod pallet {
 	#[pallet::generate_store(pub(super) trait Store)]
 	#[pallet::storage_version(STORAGE_VERSION)]
 	#[pallet::without_storage_info]
-	pub struct Pallet<T>(_);
+	pub struct Pallet<T, I = ()>(_);
 
 	#[pallet::config]
-	pub trait Config: frame_system::Config + pallet_treasury::Config {
+	pub trait Config<I: 'static = ()>: frame_system::Config + pallet_treasury::Config<I> {
 		/// The overarching event type.
-		type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
+		type Event: From<Event<Self, I>> + IsType<<Self as frame_system::Config>::Event>;
 
 		/// Maximum acceptable reason length.
 		///
@@ -136,7 +136,7 @@ pub mod pallet {
 
 		/// The amount held on deposit per byte within the tip report reason or bounty description.
 		#[pallet::constant]
-		type DataDepositPerByte: Get<BalanceOf<Self>>;
+		type DataDepositPerByte: Get<BalanceOf<Self, I>>;
 
 		/// The period for which a tip remains open after is has achieved threshold tippers.
 		#[pallet::constant]
@@ -148,7 +148,7 @@ pub mod pallet {
 
 		/// The amount held on deposit for placing a tip report.
 		#[pallet::constant]
-		type TipReportDepositBase: Get<BalanceOf<Self>>;
+		type TipReportDepositBase: Get<BalanceOf<Self, I>>;
 
 		/// Origin from which tippers must come.
 		///
@@ -166,11 +166,11 @@ pub mod pallet {
 	/// guaranteed to be a secure hash.
 	#[pallet::storage]
 	#[pallet::getter(fn tips)]
-	pub type Tips<T: Config> = StorageMap<
+	pub type Tips<T: Config<I>, I: 'static = ()> = StorageMap<
 		_,
 		Twox64Concat,
 		T::Hash,
-		OpenTip<T::AccountId, BalanceOf<T>, T::BlockNumber, T::Hash>,
+		OpenTip<T::AccountId, BalanceOf<T, I>, T::BlockNumber, T::Hash>,
 		OptionQuery,
 	>;
 
@@ -178,25 +178,26 @@ pub mod pallet {
 	/// insecure enumerable hash since the key is guaranteed to be the result of a secure hash.
 	#[pallet::storage]
 	#[pallet::getter(fn reasons)]
-	pub type Reasons<T: Config> = StorageMap<_, Identity, T::Hash, Vec<u8>, OptionQuery>;
+	pub type Reasons<T: Config<I>, I: 'static = ()> =
+		StorageMap<_, Identity, T::Hash, Vec<u8>, OptionQuery>;
 
 	#[pallet::event]
 	#[pallet::generate_deposit(pub(super) fn deposit_event)]
-	pub enum Event<T: Config> {
+	pub enum Event<T: Config<I>, I: 'static = ()> {
 		/// A new tip suggestion has been opened.
 		NewTip { tip_hash: T::Hash },
 		/// A tip suggestion has reached threshold and is closing.
 		TipClosing { tip_hash: T::Hash },
 		/// A tip suggestion has been closed.
-		TipClosed { tip_hash: T::Hash, who: T::AccountId, payout: BalanceOf<T> },
+		TipClosed { tip_hash: T::Hash, who: T::AccountId, payout: BalanceOf<T, I> },
 		/// A tip suggestion has been retracted.
 		TipRetracted { tip_hash: T::Hash },
 		/// A tip suggestion has been slashed.
-		TipSlashed { tip_hash: T::Hash, finder: T::AccountId, deposit: BalanceOf<T> },
+		TipSlashed { tip_hash: T::Hash, finder: T::AccountId, deposit: BalanceOf<T, I> },
 	}
 
 	#[pallet::error]
-	pub enum Error<T> {
+	pub enum Error<T, I = ()> {
 		/// The reason given is just too big.
 		ReasonTooBig,
 		/// The tip was already found/started.
@@ -212,7 +213,7 @@ pub mod pallet {
 	}
 
 	#[pallet::call]
-	impl<T: Config> Pallet<T> {
+	impl<T: Config<I>, I: 'static> Pallet<T, I> {
 		/// Report something `reason` that deserves a tip and claim any eventual the finder's fee.
 		///
 		/// The dispatch origin for this call must be _Signed_.
@@ -232,7 +233,7 @@ pub mod pallet {
 		/// - DbReads: `Reasons`, `Tips`
 		/// - DbWrites: `Reasons`, `Tips`
 		/// # </weight>
-		#[pallet::weight(<T as Config>::WeightInfo::report_awesome(reason.len() as u32))]
+		#[pallet::weight(<T as Config<I>>::WeightInfo::report_awesome(reason.len() as u32))]
 		pub fn report_awesome(
 			origin: OriginFor<T>,
 			reason: Vec<u8>,
@@ -242,19 +243,19 @@ pub mod pallet {
 
 			ensure!(
 				reason.len() <= T::MaximumReasonLength::get() as usize,
-				Error::<T>::ReasonTooBig
+				Error::<T, I>::ReasonTooBig
 			);
 
 			let reason_hash = T::Hashing::hash(&reason[..]);
-			ensure!(!Reasons::<T>::contains_key(&reason_hash), Error::<T>::AlreadyKnown);
+			ensure!(!Reasons::<T, I>::contains_key(&reason_hash), Error::<T, I>::AlreadyKnown);
 			let hash = T::Hashing::hash_of(&(&reason_hash, &who));
-			ensure!(!Tips::<T>::contains_key(&hash), Error::<T>::AlreadyKnown);
+			ensure!(!Tips::<T, I>::contains_key(&hash), Error::<T, I>::AlreadyKnown);
 
 			let deposit = T::TipReportDepositBase::get() +
 				T::DataDepositPerByte::get() * (reason.len() as u32).into();
 			T::Currency::reserve(&finder, deposit)?;
 
-			Reasons::<T>::insert(&reason_hash, &reason);
+			Reasons::<T, I>::insert(&reason_hash, &reason);
 			let tip = OpenTip {
 				reason: reason_hash,
 				who,
@@ -264,7 +265,7 @@ pub mod pallet {
 				tips: vec![],
 				finders_fee: true,
 			};
-			Tips::<T>::insert(&hash, tip);
+			Tips::<T, I>::insert(&hash, tip);
 			Self::deposit_event(Event::NewTip { tip_hash: hash });
 			Ok(())
 		}
@@ -288,14 +289,14 @@ pub mod pallet {
 		/// - DbReads: `Tips`, `origin account`
 		/// - DbWrites: `Reasons`, `Tips`, `origin account`
 		/// # </weight>
-		#[pallet::weight(<T as Config>::WeightInfo::retract_tip())]
+		#[pallet::weight(<T as Config<I>>::WeightInfo::retract_tip())]
 		pub fn retract_tip(origin: OriginFor<T>, hash: T::Hash) -> DispatchResult {
 			let who = ensure_signed(origin)?;
-			let tip = Tips::<T>::get(&hash).ok_or(Error::<T>::UnknownTip)?;
-			ensure!(tip.finder == who, Error::<T>::NotFinder);
+			let tip = Tips::<T, I>::get(&hash).ok_or(Error::<T, I>::UnknownTip)?;
+			ensure!(tip.finder == who, Error::<T, I>::NotFinder);
 
-			Reasons::<T>::remove(&tip.reason);
-			Tips::<T>::remove(&hash);
+			Reasons::<T, I>::remove(&tip.reason);
+			Tips::<T, I>::remove(&hash);
 			if !tip.deposit.is_zero() {
 				let err_amount = T::Currency::unreserve(&who, tip.deposit);
 				debug_assert!(err_amount.is_zero());
@@ -326,20 +327,20 @@ pub mod pallet {
 		/// - DbReads: `Tippers`, `Reasons`
 		/// - DbWrites: `Reasons`, `Tips`
 		/// # </weight>
-		#[pallet::weight(<T as Config>::WeightInfo::tip_new(reason.len() as u32, T::Tippers::max_len() as u32))]
+		#[pallet::weight(<T as Config<I>>::WeightInfo::tip_new(reason.len() as u32, T::Tippers::max_len() as u32))]
 		pub fn tip_new(
 			origin: OriginFor<T>,
 			reason: Vec<u8>,
 			who: T::AccountId,
-			#[pallet::compact] tip_value: BalanceOf<T>,
+			#[pallet::compact] tip_value: BalanceOf<T, I>,
 		) -> DispatchResult {
 			let tipper = ensure_signed(origin)?;
 			ensure!(T::Tippers::contains(&tipper), BadOrigin);
 			let reason_hash = T::Hashing::hash(&reason[..]);
-			ensure!(!Reasons::<T>::contains_key(&reason_hash), Error::<T>::AlreadyKnown);
+			ensure!(!Reasons::<T, I>::contains_key(&reason_hash), Error::<T, I>::AlreadyKnown);
 			let hash = T::Hashing::hash_of(&(&reason_hash, &who));
 
-			Reasons::<T>::insert(&reason_hash, &reason);
+			Reasons::<T, I>::insert(&reason_hash, &reason);
 			Self::deposit_event(Event::NewTip { tip_hash: hash });
 			let tips = vec![(tipper.clone(), tip_value)];
 			let tip = OpenTip {
@@ -351,7 +352,7 @@ pub mod pallet {
 				tips,
 				finders_fee: false,
 			};
-			Tips::<T>::insert(&hash, tip);
+			Tips::<T, I>::insert(&hash, tip);
 			Ok(())
 		}
 
@@ -379,20 +380,20 @@ pub mod pallet {
 		/// - DbReads: `Tippers`, `Tips`
 		/// - DbWrites: `Tips`
 		/// # </weight>
-		#[pallet::weight(<T as Config>::WeightInfo::tip(T::Tippers::max_len() as u32))]
+		#[pallet::weight(<T as Config<I>>::WeightInfo::tip(T::Tippers::max_len() as u32))]
 		pub fn tip(
 			origin: OriginFor<T>,
 			hash: T::Hash,
-			#[pallet::compact] tip_value: BalanceOf<T>,
+			#[pallet::compact] tip_value: BalanceOf<T, I>,
 		) -> DispatchResult {
 			let tipper = ensure_signed(origin)?;
 			ensure!(T::Tippers::contains(&tipper), BadOrigin);
 
-			let mut tip = Tips::<T>::get(hash).ok_or(Error::<T>::UnknownTip)?;
+			let mut tip = Tips::<T, I>::get(hash).ok_or(Error::<T, I>::UnknownTip)?;
 			if Self::insert_tip_and_check_closing(&mut tip, tipper, tip_value) {
 				Self::deposit_event(Event::TipClosing { tip_hash: hash });
 			}
-			Tips::<T>::insert(&hash, tip);
+			Tips::<T, I>::insert(&hash, tip);
 			Ok(())
 		}
 
@@ -412,16 +413,16 @@ pub mod pallet {
 		/// - DbReads: `Tips`, `Tippers`, `tip finder`
 		/// - DbWrites: `Reasons`, `Tips`, `Tippers`, `tip finder`
 		/// # </weight>
-		#[pallet::weight(<T as Config>::WeightInfo::close_tip(T::Tippers::max_len() as u32))]
+		#[pallet::weight(<T as Config<I>>::WeightInfo::close_tip(T::Tippers::max_len() as u32))]
 		pub fn close_tip(origin: OriginFor<T>, hash: T::Hash) -> DispatchResult {
 			ensure_signed(origin)?;
 
-			let tip = Tips::<T>::get(hash).ok_or(Error::<T>::UnknownTip)?;
-			let n = tip.closes.as_ref().ok_or(Error::<T>::StillOpen)?;
-			ensure!(frame_system::Pallet::<T>::block_number() >= *n, Error::<T>::Premature);
+			let tip = Tips::<T, I>::get(hash).ok_or(Error::<T, I>::UnknownTip)?;
+			let n = tip.closes.as_ref().ok_or(Error::<T, I>::StillOpen)?;
+			ensure!(frame_system::Pallet::<T>::block_number() >= *n, Error::<T, I>::Premature);
 			// closed.
-			Reasons::<T>::remove(&tip.reason);
-			Tips::<T>::remove(hash);
+			Reasons::<T, I>::remove(&tip.reason);
+			Tips::<T, I>::remove(hash);
 			Self::payout_tip(hash, tip);
 			Ok(())
 		}
@@ -438,17 +439,17 @@ pub mod pallet {
 		///   `T` is charged as upper bound given by `ContainsLengthBound`.
 		///   The actual cost depends on the implementation of `T::Tippers`.
 		/// # </weight>
-		#[pallet::weight(<T as Config>::WeightInfo::slash_tip(T::Tippers::max_len() as u32))]
+		#[pallet::weight(<T as Config<I>>::WeightInfo::slash_tip(T::Tippers::max_len() as u32))]
 		pub fn slash_tip(origin: OriginFor<T>, hash: T::Hash) -> DispatchResult {
 			T::RejectOrigin::ensure_origin(origin)?;
 
-			let tip = Tips::<T>::take(hash).ok_or(Error::<T>::UnknownTip)?;
+			let tip = Tips::<T, I>::take(hash).ok_or(Error::<T, I>::UnknownTip)?;
 
 			if !tip.deposit.is_zero() {
 				let imbalance = T::Currency::slash_reserved(&tip.finder, tip.deposit).0;
 				T::OnSlash::on_unbalanced(imbalance);
 			}
-			Reasons::<T>::remove(&tip.reason);
+			Reasons::<T, I>::remove(&tip.reason);
 			Self::deposit_event(Event::TipSlashed {
 				tip_hash: hash,
 				finder: tip.finder,
@@ -459,7 +460,7 @@ pub mod pallet {
 	}
 }
 
-impl<T: Config> Pallet<T> {
+impl<T: Config<I>, I: 'static> Pallet<T, I> {
 	// Add public immutables and private mutables.
 
 	/// The account ID of the treasury pot.
@@ -475,9 +476,9 @@ impl<T: Config> Pallet<T> {
 	///
 	/// `O(T)` and one storage access.
 	fn insert_tip_and_check_closing(
-		tip: &mut OpenTip<T::AccountId, BalanceOf<T>, T::BlockNumber, T::Hash>,
+		tip: &mut OpenTip<T::AccountId, BalanceOf<T, I>, T::BlockNumber, T::Hash>,
 		tipper: T::AccountId,
-		tip_value: BalanceOf<T>,
+		tip_value: BalanceOf<T, I>,
 	) -> bool {
 		match tip.tips.binary_search_by_key(&&tipper, |x| &x.0) {
 			Ok(pos) => tip.tips[pos] = (tipper, tip_value),
@@ -494,7 +495,7 @@ impl<T: Config> Pallet<T> {
 	}
 
 	/// Remove any non-members of `Tippers` from a `tips` vector. `O(T)`.
-	fn retain_active_tips(tips: &mut Vec<(T::AccountId, BalanceOf<T>)>) {
+	fn retain_active_tips(tips: &mut Vec<(T::AccountId, BalanceOf<T, I>)>) {
 		let members = T::Tippers::sorted_members();
 		let mut members_iter = members.iter();
 		let mut member = members_iter.next();
@@ -520,14 +521,14 @@ impl<T: Config> Pallet<T> {
 	/// Plus `O(T)` (`T` is Tippers length).
 	fn payout_tip(
 		hash: T::Hash,
-		tip: OpenTip<T::AccountId, BalanceOf<T>, T::BlockNumber, T::Hash>,
+		tip: OpenTip<T::AccountId, BalanceOf<T, I>, T::BlockNumber, T::Hash>,
 	) {
 		let mut tips = tip.tips;
 		Self::retain_active_tips(&mut tips);
 		tips.sort_by_key(|i| i.1);
 
 		let treasury = Self::account_id();
-		let max_payout = pallet_treasury::Pallet::<T>::pot();
+		let max_payout = pallet_treasury::Pallet::<T, I>::pot();
 
 		let mut payout = tips[tips.len() / 2].1.min(max_payout);
 		if !tip.deposit.is_zero() {
@@ -582,7 +583,7 @@ impl<T: Config> Pallet<T> {
 
 		for (hash, old_tip) in storage_key_iter::<
 			T::Hash,
-			OldOpenTip<T::AccountId, BalanceOf<T>, T::BlockNumber, T::Hash>,
+			OldOpenTip<T::AccountId, BalanceOf<T, I>, T::BlockNumber, T::Hash>,
 			Twox64Concat,
 		>(module, item)
 		.drain()
@@ -600,7 +601,7 @@ impl<T: Config> Pallet<T> {
 				tips: old_tip.tips,
 				finders_fee,
 			};
-			Tips::<T>::insert(hash, new_tip)
+			Tips::<T, I>::insert(hash, new_tip)
 		}
 	}
 }
diff --git a/substrate/frame/tips/src/tests.rs b/substrate/frame/tips/src/tests.rs
index 235952fd109..194ecc60d98 100644
--- a/substrate/frame/tips/src/tests.rs
+++ b/substrate/frame/tips/src/tests.rs
@@ -25,7 +25,7 @@ use sp_core::H256;
 use sp_runtime::{
 	testing::Header,
 	traits::{BadOrigin, BlakeTwo256, IdentityLookup},
-	Perbill, Permill,
+	BuildStorage, Perbill, Permill,
 };
 use sp_storage::Storage;
 
@@ -53,7 +53,9 @@ frame_support::construct_runtime!(
 		System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
 		Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
 		Treasury: pallet_treasury::{Pallet, Call, Storage, Config, Event<T>},
+		Treasury1: pallet_treasury::<Instance1>::{Pallet, Call, Storage, Config, Event<T>},
 		Tips: pallet_tips::{Pallet, Call, Storage, Event<T>},
+		Tips1: pallet_tips::<Instance1>::{Pallet, Call, Storage, Event<T>},
 	}
 );
 
@@ -127,6 +129,7 @@ parameter_types! {
 	pub const ProposalBond: Permill = Permill::from_percent(5);
 	pub const Burn: Permill = Permill::from_percent(50);
 	pub const TreasuryPalletId: PalletId = PalletId(*b"py/trsry");
+	pub const TreasuryPalletId2: PalletId = PalletId(*b"py/trsr2");
 }
 impl pallet_treasury::Config for Test {
 	type PalletId = TreasuryPalletId;
@@ -146,6 +149,26 @@ impl pallet_treasury::Config for Test {
 	type MaxApprovals = ConstU32<100>;
 	type SpendOrigin = frame_support::traits::NeverEnsureOrigin<u64>;
 }
+
+impl pallet_treasury::Config<Instance1> for Test {
+	type PalletId = TreasuryPalletId2;
+	type Currency = pallet_balances::Pallet<Test>;
+	type ApproveOrigin = frame_system::EnsureRoot<u128>;
+	type RejectOrigin = frame_system::EnsureRoot<u128>;
+	type Event = Event;
+	type OnSlash = ();
+	type ProposalBond = ProposalBond;
+	type ProposalBondMinimum = ConstU64<1>;
+	type ProposalBondMaximum = ();
+	type SpendPeriod = ConstU64<2>;
+	type Burn = Burn;
+	type BurnDestination = (); // Just gets burned.
+	type WeightInfo = ();
+	type SpendFunds = ();
+	type MaxApprovals = ConstU32<100>;
+	type SpendOrigin = frame_support::traits::NeverEnsureOrigin<u64>;
+}
+
 parameter_types! {
 	pub const TipFindersFee: Percent = Percent::from_percent(20);
 }
@@ -160,16 +183,29 @@ impl Config for Test {
 	type WeightInfo = ();
 }
 
+impl Config<Instance1> for Test {
+	type MaximumReasonLength = ConstU32<16384>;
+	type Tippers = TenToFourteen;
+	type TipCountdown = ConstU64<1>;
+	type TipFindersFee = TipFindersFee;
+	type TipReportDepositBase = ConstU64<1>;
+	type DataDepositPerByte = ConstU64<1>;
+	type Event = Event;
+	type WeightInfo = ();
+}
+
 pub fn new_test_ext() -> sp_io::TestExternalities {
-	let mut t = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap();
-	pallet_balances::GenesisConfig::<Test> {
-		// Total issuance will be 200 with treasury account initialized at ED.
-		balances: vec![(0, 100), (1, 98), (2, 1)],
+	let mut ext: sp_io::TestExternalities = GenesisConfig {
+		system: frame_system::GenesisConfig::default(),
+		balances: pallet_balances::GenesisConfig { balances: vec![(0, 100), (1, 98), (2, 1)] },
+		treasury: Default::default(),
+		treasury_1: Default::default(),
 	}
-	.assimilate_storage(&mut t)
-	.unwrap();
-	GenesisBuild::<Test>::assimilate_storage(&pallet_treasury::GenesisConfig, &mut t).unwrap();
-	t.into()
+	.build_storage()
+	.unwrap()
+	.into();
+	ext.execute_with(|| System::set_block_number(1));
+	ext
 }
 
 fn last_event() -> TipEvent<Test> {
@@ -540,3 +576,36 @@ fn genesis_funding_works() {
 		assert_eq!(Treasury::pot(), initial_funding - Balances::minimum_balance());
 	});
 }
+
+#[test]
+fn report_awesome_and_tip_works_second_instance() {
+	new_test_ext().execute_with(|| {
+		Balances::make_free_balance_be(&Treasury::account_id(), 101);
+		Balances::make_free_balance_be(&Treasury1::account_id(), 201);
+		assert_eq!(Balances::free_balance(&Treasury::account_id()), 101);
+		assert_eq!(Balances::free_balance(&Treasury1::account_id()), 201);
+
+		assert_ok!(Tips1::report_awesome(Origin::signed(0), b"awesome.dot".to_vec(), 3));
+		// duplicate report in tips1 reports don't count.
+		assert_noop!(
+			Tips1::report_awesome(Origin::signed(1), b"awesome.dot".to_vec(), 3),
+			Error::<Test, Instance1>::AlreadyKnown
+		);
+		// but tips is separate
+		assert_ok!(Tips::report_awesome(Origin::signed(0), b"awesome.dot".to_vec(), 3));
+
+		let h = tip_hash();
+		assert_ok!(Tips1::tip(Origin::signed(10), h.clone(), 10));
+		assert_ok!(Tips1::tip(Origin::signed(11), h.clone(), 10));
+		assert_ok!(Tips1::tip(Origin::signed(12), h.clone(), 10));
+		assert_noop!(Tips1::tip(Origin::signed(9), h.clone(), 10), BadOrigin);
+
+		System::set_block_number(2);
+
+		assert_ok!(Tips1::close_tip(Origin::signed(100), h.into()));
+		// Treasury 1 unchanged
+		assert_eq!(Balances::free_balance(&Treasury::account_id()), 101);
+		// Treasury 2 gave the funds
+		assert_eq!(Balances::free_balance(&Treasury1::account_id()), 191);
+	});
+}
-- 
GitLab