From da40d97a2311640fe3f0da486f2ebf4cb840f40f Mon Sep 17 00:00:00 2001
From: Muharem <ismailov.m.h@gmail.com>
Date: Fri, 8 Dec 2023 14:02:09 +0100
Subject: [PATCH] Westend: Fellowship Treasury (#2532)

Treasury Pallet Instance for the Fellowship in Westend Collectives.

In this update, we present a Treasury Pallet Instance that is under the
control of the Fellowship body, with oversight from the Root and
Treasurer origins. Here's how it is governed:
- the Root origin have the authority to reject or approve spend
proposals, with no amount limit for approvals.
- the Treasurer origin have the authority to reject or approve spend
proposals, with approval limits of up to 10,000,000 DOT.
- Voice of all Fellows ranked at 3 or above can reject or approve spend
proposals, with a maximum approval limit of 10,000 DOT.
- Voice of Fellows ranked at 4 or above can also reject or approve spend
proposals, with a maximum approval limit of 10,000,000 DOT.

Additionally, we introduce the Asset Rate Pallet Instance to establish
conversion rates from asset A to B. This is used to determine if a
proposed spend amount involving a non-native asset is permissible by the
commanding origin. The rates can be set up by the Root, Treasurer
origins, or Voice of all Fellows.

---------

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>
Co-authored-by: joepetrowski <joe@parity.io>
---
 Cargo.lock                                    |  19 ++
 Cargo.toml                                    |   1 +
 cumulus/parachains/common/src/polkadot.rs     |   2 +
 cumulus/parachains/common/src/westend.rs      |   2 +
 .../collectives-westend/Cargo.toml            |  25 ++
 .../collectives-westend/src/genesis.rs        |  67 ++++++
 .../collectives-westend/src/lib.rs            |  51 +++++
 .../networks/westend-system/Cargo.toml        |   1 +
 .../networks/westend-system/src/lib.rs        |   4 +
 .../tests/assets/asset-hub-westend/Cargo.toml |   1 +
 .../tests/assets/asset-hub-westend/src/lib.rs |   7 +-
 .../src/tests/fellowship_treasury.rs          | 131 +++++++++++
 .../assets/asset-hub-westend/src/tests/mod.rs |   1 +
 .../asset-hub-westend/src/xcm_config.rs       |  16 ++
 .../collectives-westend/Cargo.toml            |   8 +
 .../collectives-westend/src/fellowship/mod.rs | 128 ++++++++++-
 .../collectives-westend/src/lib.rs            |  27 ++-
 .../collectives-westend/src/weights/mod.rs    |   2 +
 .../src/weights/pallet_asset_rate.rs          |  85 +++++++
 .../src/weights/pallet_treasury.rs            | 214 ++++++++++++++++++
 .../collectives-westend/src/xcm_config.rs     |   6 +-
 polkadot/runtime/common/src/impls.rs          |  21 +-
 polkadot/runtime/westend/constants/src/lib.rs |   1 +
 polkadot/runtime/westend/src/xcm_config.rs    |  13 +-
 prdoc/pr_2532.prdoc                           |  11 +
 substrate/frame/treasury/src/benchmarking.rs  |  10 +-
 26 files changed, 827 insertions(+), 27 deletions(-)
 create mode 100644 cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend/Cargo.toml
 create mode 100644 cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend/src/genesis.rs
 create mode 100644 cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend/src/lib.rs
 create mode 100644 cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/fellowship_treasury.rs
 create mode 100644 cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_asset_rate.rs
 create mode 100644 cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_treasury.rs
 create mode 100644 prdoc/pr_2532.prdoc

diff --git a/Cargo.lock b/Cargo.lock
index 1dd20a12849..ee815868957 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -885,6 +885,7 @@ dependencies = [
  "asset-test-utils",
  "cumulus-pallet-dmp-queue",
  "cumulus-pallet-parachain-system",
+ "cumulus-pallet-xcmp-queue",
  "emulated-integration-tests-common",
  "frame-support",
  "frame-system",
@@ -2613,6 +2614,21 @@ dependencies = [
  "unicode-width",
 ]
 
+[[package]]
+name = "collectives-westend-emulated-chain"
+version = "0.0.0"
+dependencies = [
+ "collectives-westend-runtime",
+ "cumulus-primitives-core",
+ "emulated-integration-tests-common",
+ "frame-support",
+ "parachains-common",
+ "serde_json",
+ "sp-core",
+ "sp-runtime",
+ "westend-emulated-chain",
+]
+
 [[package]]
 name = "collectives-westend-runtime"
 version = "1.0.0"
@@ -2634,6 +2650,7 @@ dependencies = [
  "hex-literal",
  "log",
  "pallet-alliance",
+ "pallet-asset-rate",
  "pallet-aura",
  "pallet-authorship",
  "pallet-balances",
@@ -2653,6 +2670,7 @@ dependencies = [
  "pallet-timestamp",
  "pallet-transaction-payment",
  "pallet-transaction-payment-rpc-runtime-api",
+ "pallet-treasury",
  "pallet-utility",
  "pallet-xcm",
  "parachains-common",
@@ -21049,6 +21067,7 @@ version = "0.0.0"
 dependencies = [
  "asset-hub-westend-emulated-chain",
  "bridge-hub-westend-emulated-chain",
+ "collectives-westend-emulated-chain",
  "emulated-integration-tests-common",
  "penpal-emulated-chain",
  "westend-emulated-chain",
diff --git a/Cargo.toml b/Cargo.toml
index 26d48ba0ced..1c791b6118f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -66,6 +66,7 @@ members = [
 	"cumulus/parachains/integration-tests/emulated/chains/parachains/assets/asset-hub-westend",
 	"cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-rococo",
 	"cumulus/parachains/integration-tests/emulated/chains/parachains/bridges/bridge-hub-westend",
+	"cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend",
 	"cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal",
 	"cumulus/parachains/integration-tests/emulated/chains/relays/rococo",
 	"cumulus/parachains/integration-tests/emulated/chains/relays/westend",
diff --git a/cumulus/parachains/common/src/polkadot.rs b/cumulus/parachains/common/src/polkadot.rs
index 744108bce2e..ca413830342 100644
--- a/cumulus/parachains/common/src/polkadot.rs
+++ b/cumulus/parachains/common/src/polkadot.rs
@@ -31,6 +31,8 @@ pub mod account {
 	/// It is used as a temporarily place to deposit a slashed imbalance
 	/// before the teleport to the Treasury.
 	pub const AMBASSADOR_REFERENDA_PALLET_ID: PalletId = PalletId(*b"py/amref");
+	/// Fellowship treasury pallet ID
+	pub const FELLOWSHIP_TREASURY_PALLET_ID: PalletId = PalletId(*b"py/feltr");
 }
 
 /// Consensus-related.
diff --git a/cumulus/parachains/common/src/westend.rs b/cumulus/parachains/common/src/westend.rs
index 0ae21e23454..2bd4d18a15e 100644
--- a/cumulus/parachains/common/src/westend.rs
+++ b/cumulus/parachains/common/src/westend.rs
@@ -29,6 +29,8 @@ pub mod account {
 	/// Ambassador Referenda pallet ID - used as a temporary place to deposit a slashed imbalance
 	/// before the teleport to the Treasury.
 	pub const AMBASSADOR_REFERENDA_PALLET_ID: PalletId = PalletId(*b"py/amref");
+	/// Fellowship treasury pallet ID.
+	pub const FELLOWSHIP_TREASURY_PALLET_ID: PalletId = PalletId(*b"py/feltr");
 }
 
 pub mod currency {
diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend/Cargo.toml
new file mode 100644
index 00000000000..5dcf139bdb7
--- /dev/null
+++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend/Cargo.toml
@@ -0,0 +1,25 @@
+[package]
+name = "collectives-westend-emulated-chain"
+version = "0.0.0"
+authors.workspace = true
+edition.workspace = true
+license = "Apache-2.0"
+description = "Collectives Westend emulated chain"
+publish = false
+
+[dependencies]
+serde_json = "1.0.104"
+
+# Substrate
+sp-core = { path = "../../../../../../../../substrate/primitives/core", default-features = false }
+sp-runtime = { path = "../../../../../../../../substrate/primitives/runtime", default-features = false }
+frame-support = { path = "../../../../../../../../substrate/frame/support", default-features = false }
+
+# Polakadot
+parachains-common = { path = "../../../../../../../parachains/common" }
+
+# Cumulus
+cumulus-primitives-core = { path = "../../../../../../../primitives/core", default-features = false }
+emulated-integration-tests-common = { path = "../../../../common", default-features = false }
+collectives-westend-runtime = { path = "../../../../../../runtimes/collectives/collectives-westend" }
+westend-emulated-chain = { path = "../../../relays/westend" }
diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend/src/genesis.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend/src/genesis.rs
new file mode 100644
index 00000000000..d79ef55072a
--- /dev/null
+++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend/src/genesis.rs
@@ -0,0 +1,67 @@
+// Copyright (C) Parity Technologies (UK) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 	http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Substrate
+use sp_core::storage::Storage;
+
+// Cumulus
+use emulated_integration_tests_common::{
+	accounts, build_genesis_storage, collators, SAFE_XCM_VERSION,
+};
+use parachains_common::Balance;
+
+pub const PARA_ID: u32 = 1001;
+pub const ED: Balance = parachains_common::westend::currency::EXISTENTIAL_DEPOSIT;
+
+pub fn genesis() -> Storage {
+	let genesis_config = collectives_westend_runtime::RuntimeGenesisConfig {
+		system: collectives_westend_runtime::SystemConfig::default(),
+		balances: collectives_westend_runtime::BalancesConfig {
+			balances: accounts::init_balances().iter().cloned().map(|k| (k, ED * 4096)).collect(),
+		},
+		parachain_info: collectives_westend_runtime::ParachainInfoConfig {
+			parachain_id: PARA_ID.into(),
+			..Default::default()
+		},
+		collator_selection: collectives_westend_runtime::CollatorSelectionConfig {
+			invulnerables: collators::invulnerables().iter().cloned().map(|(acc, _)| acc).collect(),
+			candidacy_bond: ED * 16,
+			..Default::default()
+		},
+		session: collectives_westend_runtime::SessionConfig {
+			keys: collators::invulnerables()
+				.into_iter()
+				.map(|(acc, aura)| {
+					(
+						acc.clone(),                                       // account id
+						acc,                                               // validator id
+						collectives_westend_runtime::SessionKeys { aura }, // session keys
+					)
+				})
+				.collect(),
+		},
+		polkadot_xcm: collectives_westend_runtime::PolkadotXcmConfig {
+			safe_xcm_version: Some(SAFE_XCM_VERSION),
+			..Default::default()
+		},
+		..Default::default()
+	};
+
+	build_genesis_storage(
+		&genesis_config,
+		collectives_westend_runtime::WASM_BINARY
+			.expect("WASM binary was not built, please build it!"),
+	)
+}
diff --git a/cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend/src/lib.rs
new file mode 100644
index 00000000000..5d553b6f103
--- /dev/null
+++ b/cumulus/parachains/integration-tests/emulated/chains/parachains/collectives/collectives-westend/src/lib.rs
@@ -0,0 +1,51 @@
+// Copyright (C) Parity Technologies (UK) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 	http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+pub mod genesis;
+
+// Substrate
+use frame_support::traits::OnInitialize;
+
+// Cumulus
+use emulated_integration_tests_common::{
+	impl_accounts_helpers_for_parachain, impl_assert_events_helpers_for_parachain,
+	impls::Parachain, xcm_emulator::decl_test_parachains,
+};
+
+// CollectivesWestend Parachain declaration
+decl_test_parachains! {
+	pub struct CollectivesWestend {
+		genesis = genesis::genesis(),
+		on_init = {
+			collectives_westend_runtime::AuraExt::on_initialize(1);
+		},
+		runtime = collectives_westend_runtime,
+		core = {
+			XcmpMessageHandler: collectives_westend_runtime::XcmpQueue,
+			LocationToAccountId: collectives_westend_runtime::xcm_config::LocationToAccountId,
+			ParachainInfo: collectives_westend_runtime::ParachainInfo,
+		},
+		pallets = {
+			PolkadotXcm: collectives_westend_runtime::PolkadotXcm,
+			Balances: collectives_westend_runtime::Balances,
+			FellowshipTreasury: collectives_westend_runtime::FellowshipTreasury,
+			AssetRate: collectives_westend_runtime::AssetRate,
+		}
+	},
+}
+
+// AssetHubWestend implementation
+impl_accounts_helpers_for_parachain!(CollectivesWestend);
+impl_assert_events_helpers_for_parachain!(CollectivesWestend);
diff --git a/cumulus/parachains/integration-tests/emulated/networks/westend-system/Cargo.toml b/cumulus/parachains/integration-tests/emulated/networks/westend-system/Cargo.toml
index a4360076d6b..634111bc381 100644
--- a/cumulus/parachains/integration-tests/emulated/networks/westend-system/Cargo.toml
+++ b/cumulus/parachains/integration-tests/emulated/networks/westend-system/Cargo.toml
@@ -13,4 +13,5 @@ emulated-integration-tests-common = { path = "../../common", default-features =
 westend-emulated-chain = { path = "../../chains/relays/westend", default-features = false }
 asset-hub-westend-emulated-chain = { path = "../../chains/parachains/assets/asset-hub-westend" }
 bridge-hub-westend-emulated-chain = { path = "../../chains/parachains/bridges/bridge-hub-westend" }
+collectives-westend-emulated-chain = { path = "../../chains/parachains/collectives/collectives-westend" }
 penpal-emulated-chain = { path = "../../chains/parachains/testing/penpal" }
diff --git a/cumulus/parachains/integration-tests/emulated/networks/westend-system/src/lib.rs b/cumulus/parachains/integration-tests/emulated/networks/westend-system/src/lib.rs
index 667b44a6986..26cd5c7e860 100644
--- a/cumulus/parachains/integration-tests/emulated/networks/westend-system/src/lib.rs
+++ b/cumulus/parachains/integration-tests/emulated/networks/westend-system/src/lib.rs
@@ -15,11 +15,13 @@
 
 pub use asset_hub_westend_emulated_chain;
 pub use bridge_hub_westend_emulated_chain;
+pub use collectives_westend_emulated_chain;
 pub use penpal_emulated_chain;
 pub use westend_emulated_chain;
 
 use asset_hub_westend_emulated_chain::AssetHubWestend;
 use bridge_hub_westend_emulated_chain::BridgeHubWestend;
+use collectives_westend_emulated_chain::CollectivesWestend;
 use penpal_emulated_chain::{PenpalA, PenpalB};
 use westend_emulated_chain::Westend;
 
@@ -35,6 +37,7 @@ decl_test_networks! {
 		parachains = vec![
 			AssetHubWestend,
 			BridgeHubWestend,
+			CollectivesWestend,
 			PenpalA,
 			PenpalB,
 		],
@@ -46,6 +49,7 @@ decl_test_sender_receiver_accounts_parameter_types! {
 	WestendRelay { sender: ALICE, receiver: BOB },
 	AssetHubWestendPara { sender: ALICE, receiver: BOB },
 	BridgeHubWestendPara { sender: ALICE, receiver: BOB },
+	CollectivesWestendPara { sender: ALICE, receiver: BOB },
 	PenpalAPara { sender: ALICE, receiver: BOB },
 	PenpalBPara { sender: ALICE, receiver: BOB }
 }
diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml
index cdaa65f02cb..a0861b49955 100644
--- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml
+++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/Cargo.toml
@@ -36,6 +36,7 @@ parachains-common = { path = "../../../../../../parachains/common" }
 asset-hub-westend-runtime = { path = "../../../../../runtimes/assets/asset-hub-westend" }
 asset-test-utils = { path = "../../../../../runtimes/assets/test-utils" }
 cumulus-pallet-dmp-queue = { default-features = false, path = "../../../../../../pallets/dmp-queue" }
+cumulus-pallet-xcmp-queue = { default-features = false, path = "../../../../../../pallets/xcmp-queue" }
 cumulus-pallet-parachain-system = { default-features = false, path = "../../../../../../pallets/parachain-system" }
 emulated-integration-tests-common = { path = "../../../common", default-features = false }
 westend-system-emulated-network = { path = "../../../networks/westend-system" }
diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs
index e2c03d2f8f0..e9c7a59faaf 100644
--- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs
+++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/lib.rs
@@ -47,11 +47,16 @@ pub use westend_system_emulated_network::{
 	asset_hub_westend_emulated_chain::{
 		genesis::ED as ASSET_HUB_WESTEND_ED, AssetHubWestendParaPallet as AssetHubWestendPallet,
 	},
+	collectives_westend_emulated_chain::{
+		genesis::ED as COLLECTIVES_WESTEND_ED,
+		CollectivesWestendParaPallet as CollectivesWestendPallet,
+	},
 	penpal_emulated_chain::PenpalBParaPallet as PenpalBPallet,
 	westend_emulated_chain::{genesis::ED as WESTEND_ED, WestendRelayPallet as WestendPallet},
 	AssetHubWestendPara as AssetHubWestend, AssetHubWestendParaReceiver as AssetHubWestendReceiver,
 	AssetHubWestendParaSender as AssetHubWestendSender, BridgeHubWestendPara as BridgeHubWestend,
-	BridgeHubWestendParaReceiver as BridgeHubWestendReceiver, PenpalBPara as PenpalB,
+	BridgeHubWestendParaReceiver as BridgeHubWestendReceiver,
+	CollectivesWestendPara as CollectivesWestend, PenpalBPara as PenpalB,
 	PenpalBParaReceiver as PenpalBReceiver, PenpalBParaSender as PenpalBSender,
 	WestendRelay as Westend, WestendRelayReceiver as WestendReceiver,
 	WestendRelaySender as WestendSender,
diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/fellowship_treasury.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/fellowship_treasury.rs
new file mode 100644
index 00000000000..d7de0a451f2
--- /dev/null
+++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/fellowship_treasury.rs
@@ -0,0 +1,131 @@
+// Copyright (C) Parity Technologies (UK) Ltd.
+// SPDX-License-Identifier: Apache-2.0
+
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// 	http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use crate::*;
+use emulated_integration_tests_common::accounts::{ALICE, BOB};
+use frame_support::traits::fungibles::{Create, Inspect, Mutate};
+use polkadot_runtime_common::impls::VersionedLocatableAsset;
+use xcm_executor::traits::ConvertLocation;
+
+#[test]
+fn create_and_claim_treasury_spend() {
+	const ASSET_ID: u32 = 1984;
+	const SPEND_AMOUNT: u128 = 1_000_000;
+	// treasury location from a sibling parachain.
+	let treasury_location: MultiLocation = MultiLocation::new(
+		1,
+		X2(Parachain(CollectivesWestend::para_id().into()), PalletInstance(65)),
+	);
+	// treasury account on a sibling parachain.
+	let treasury_account =
+		asset_hub_westend_runtime::xcm_config::LocationToAccountId::convert_location(
+			&treasury_location,
+		)
+		.unwrap();
+	let asset_hub_location = MultiLocation::new(1, Parachain(AssetHubWestend::para_id().into()));
+	let root = <CollectivesWestend as Chain>::RuntimeOrigin::root();
+	// asset kind to be spent from the treasury.
+	let asset_kind = VersionedLocatableAsset::V3 {
+		location: asset_hub_location,
+		asset_id: AssetId::Concrete((PalletInstance(50), GeneralIndex(ASSET_ID.into())).into()),
+	};
+	// treasury spend beneficiary.
+	let alice: AccountId = Westend::account_id_of(ALICE);
+	let bob: AccountId = CollectivesWestend::account_id_of(BOB);
+	let bob_signed = <CollectivesWestend as Chain>::RuntimeOrigin::signed(bob.clone());
+
+	AssetHubWestend::execute_with(|| {
+		type Assets = <AssetHubWestend as AssetHubWestendPallet>::Assets;
+
+		// create an asset class and mint some assets to the treasury account.
+		assert_ok!(<Assets as Create<_>>::create(
+			ASSET_ID,
+			treasury_account.clone(),
+			true,
+			SPEND_AMOUNT / 2
+		));
+		assert_ok!(<Assets as Mutate<_>>::mint_into(ASSET_ID, &treasury_account, SPEND_AMOUNT * 4));
+		// beneficiary has zero balance.
+		assert_eq!(<Assets as Inspect<_>>::balance(ASSET_ID, &alice,), 0u128,);
+	});
+
+	CollectivesWestend::execute_with(|| {
+		type RuntimeEvent = <CollectivesWestend as Chain>::RuntimeEvent;
+		type FellowshipTreasury =
+			<CollectivesWestend as CollectivesWestendPallet>::FellowshipTreasury;
+		type AssetRate = <CollectivesWestend as CollectivesWestendPallet>::AssetRate;
+
+		// create a conversion rate from `asset_kind` to the native currency.
+		assert_ok!(AssetRate::create(root.clone(), Box::new(asset_kind.clone()), 2.into()));
+
+		// create and approve a treasury spend.
+		assert_ok!(FellowshipTreasury::spend(
+			root,
+			Box::new(asset_kind),
+			SPEND_AMOUNT,
+			Box::new(MultiLocation::new(0, Into::<[u8; 32]>::into(alice.clone())).into()),
+			None,
+		));
+		// claim the spend.
+		assert_ok!(FellowshipTreasury::payout(bob_signed.clone(), 0));
+
+		assert_expected_events!(
+			CollectivesWestend,
+			vec![
+				RuntimeEvent::FellowshipTreasury(pallet_treasury::Event::Paid { .. }) => {},
+			]
+		);
+	});
+
+	AssetHubWestend::execute_with(|| {
+		type RuntimeEvent = <AssetHubWestend as Chain>::RuntimeEvent;
+		type Assets = <AssetHubWestend as AssetHubWestendPallet>::Assets;
+
+		// assert events triggered by xcm pay program
+		// 1. treasury asset transferred to spend beneficiary
+		// 2. response to the Fellowship treasury pallet instance sent back
+		// 3. XCM program completed
+		assert_expected_events!(
+			AssetHubWestend,
+			vec![
+				RuntimeEvent::Assets(pallet_assets::Event::Transferred { asset_id: id, from, to, amount }) => {
+					id: id == &ASSET_ID,
+					from: from == &treasury_account,
+					to: to == &alice,
+					amount: amount == &SPEND_AMOUNT,
+				},
+				RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},
+				RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { success: true ,.. }) => {},
+			]
+		);
+		// beneficiary received the assets from the treasury.
+		assert_eq!(<Assets as Inspect<_>>::balance(ASSET_ID, &alice,), SPEND_AMOUNT,);
+	});
+
+	CollectivesWestend::execute_with(|| {
+		type RuntimeEvent = <CollectivesWestend as Chain>::RuntimeEvent;
+		type FellowshipTreasury =
+			<CollectivesWestend as CollectivesWestendPallet>::FellowshipTreasury;
+
+		// check the payment status to ensure the response from the AssetHub was received.
+		assert_ok!(FellowshipTreasury::check_status(bob_signed, 0));
+		assert_expected_events!(
+			CollectivesWestend,
+			vec![
+				RuntimeEvent::FellowshipTreasury(pallet_treasury::Event::SpendProcessed { .. }) => {},
+			]
+		);
+	});
+}
diff --git a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/mod.rs b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/mod.rs
index d2127b63048..ee720c24480 100644
--- a/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/mod.rs
+++ b/cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend/src/tests/mod.rs
@@ -13,6 +13,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+mod fellowship_treasury;
 mod reserve_transfer;
 mod send;
 mod set_xcm_versions;
diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs
index 19fd9b0b928..946ab9696f7 100644
--- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs
+++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs
@@ -239,6 +239,18 @@ match_types! {
 		MultiLocation { parents: 1, interior: Here } |
 		MultiLocation { parents: 1, interior: X1(Plurality { .. }) }
 	};
+	pub type FellowshipEntities: impl Contains<MultiLocation> = {
+		// Fellowship Plurality
+		MultiLocation { parents: 1, interior: X2(Parachain(1001), Plurality { id: BodyId::Technical, ..}) } |
+		// Fellowship Salary Pallet
+		MultiLocation { parents: 1, interior: X2(Parachain(1001), PalletInstance(64)) } |
+		// Fellowship Treasury Pallet
+		MultiLocation { parents: 1, interior: X2(Parachain(1001), PalletInstance(65)) }
+	};
+	pub type AmbassadorEntities: impl Contains<MultiLocation> = {
+		// Ambassador Salary Pallet
+		MultiLocation { parents: 1, interior: X2(Parachain(1001), PalletInstance(74)) }
+	};
 }
 
 /// A call filter for the XCM Transact instruction. This is a temporary measure until we properly
@@ -487,6 +499,8 @@ pub type Barrier = TrailingSetTopicAsId<
 						ParentOrParentsPlurality,
 						Equals<RelayTreasuryLocation>,
 						Equals<bridging::SiblingBridgeHub>,
+						FellowshipEntities,
+						AmbassadorEntities,
 					)>,
 					// Subscriptions for version tracking are OK.
 					AllowSubscriptionsFrom<Everything>,
@@ -525,6 +539,8 @@ pub type ForeignAssetFeeAsExistentialDepositMultiplierFeeCharger =
 pub type WaivedLocations = (
 	RelayOrOtherSystemParachains<AllSiblingSystemParachains, Runtime>,
 	Equals<RelayTreasuryLocation>,
+	FellowshipEntities,
+	AmbassadorEntities,
 );
 
 /// Cases where a remote origin is accepted as trusted Teleporter for a given asset:
diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/Cargo.toml b/cumulus/parachains/runtimes/collectives/collectives-westend/Cargo.toml
index 94a5edc371f..433e55c6ea9 100644
--- a/cumulus/parachains/runtimes/collectives/collectives-westend/Cargo.toml
+++ b/cumulus/parachains/runtimes/collectives/collectives-westend/Cargo.toml
@@ -21,6 +21,7 @@ frame-system = { path = "../../../../../substrate/frame/system", default-feature
 frame-system-benchmarking = { path = "../../../../../substrate/frame/system/benchmarking", default-features = false, optional = true }
 frame-system-rpc-runtime-api = { path = "../../../../../substrate/frame/system/rpc/runtime-api", default-features = false }
 frame-try-runtime = { path = "../../../../../substrate/frame/try-runtime", default-features = false, optional = true }
+pallet-asset-rate = { path = "../../../../../substrate/frame/asset-rate", default-features = false }
 pallet-alliance = { path = "../../../../../substrate/frame/alliance", default-features = false }
 pallet-aura = { path = "../../../../../substrate/frame/aura", default-features = false }
 pallet-authorship = { path = "../../../../../substrate/frame/authorship", default-features = false }
@@ -34,6 +35,7 @@ pallet-session = { path = "../../../../../substrate/frame/session", default-feat
 pallet-timestamp = { path = "../../../../../substrate/frame/timestamp", default-features = false }
 pallet-transaction-payment = { path = "../../../../../substrate/frame/transaction-payment", default-features = false }
 pallet-transaction-payment-rpc-runtime-api = { path = "../../../../../substrate/frame/transaction-payment/rpc/runtime-api", default-features = false }
+pallet-treasury = { path = "../../../../../substrate/frame/treasury", default-features = false }
 pallet-utility = { path = "../../../../../substrate/frame/utility", default-features = false }
 pallet-referenda = { path = "../../../../../substrate/frame/referenda", default-features = false }
 pallet-ranked-collective = { path = "../../../../../substrate/frame/ranked-collective", default-features = false }
@@ -97,6 +99,7 @@ runtime-benchmarks = [
 	"frame-system-benchmarking/runtime-benchmarks",
 	"frame-system/runtime-benchmarks",
 	"pallet-alliance/runtime-benchmarks",
+	"pallet-asset-rate/runtime-benchmarks",
 	"pallet-balances/runtime-benchmarks",
 	"pallet-collator-selection/runtime-benchmarks",
 	"pallet-collective-content/runtime-benchmarks",
@@ -111,6 +114,7 @@ runtime-benchmarks = [
 	"pallet-salary/runtime-benchmarks",
 	"pallet-scheduler/runtime-benchmarks",
 	"pallet-timestamp/runtime-benchmarks",
+	"pallet-treasury/runtime-benchmarks",
 	"pallet-utility/runtime-benchmarks",
 	"pallet-xcm/runtime-benchmarks",
 	"parachains-common/runtime-benchmarks",
@@ -130,6 +134,7 @@ try-runtime = [
 	"frame-system/try-runtime",
 	"frame-try-runtime/try-runtime",
 	"pallet-alliance/try-runtime",
+	"pallet-asset-rate/try-runtime",
 	"pallet-aura/try-runtime",
 	"pallet-authorship/try-runtime",
 	"pallet-balances/try-runtime",
@@ -148,6 +153,7 @@ try-runtime = [
 	"pallet-session/try-runtime",
 	"pallet-timestamp/try-runtime",
 	"pallet-transaction-payment/try-runtime",
+	"pallet-treasury/try-runtime",
 	"pallet-utility/try-runtime",
 	"pallet-xcm/try-runtime",
 	"parachain-info/try-runtime",
@@ -172,6 +178,7 @@ std = [
 	"frame-try-runtime?/std",
 	"log/std",
 	"pallet-alliance/std",
+	"pallet-asset-rate/std",
 	"pallet-aura/std",
 	"pallet-authorship/std",
 	"pallet-balances/std",
@@ -191,6 +198,7 @@ std = [
 	"pallet-timestamp/std",
 	"pallet-transaction-payment-rpc-runtime-api/std",
 	"pallet-transaction-payment/std",
+	"pallet-treasury/std",
 	"pallet-utility/std",
 	"pallet-xcm/std",
 	"parachain-info/std",
diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/fellowship/mod.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/fellowship/mod.rs
index b7412705dde..3fd108c0a5c 100644
--- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/fellowship/mod.rs
+++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/fellowship/mod.rs
@@ -21,28 +21,41 @@ mod tracks;
 use crate::{
 	impls::ToParentTreasury,
 	weights,
-	xcm_config::{FellowshipAdminBodyId, UsdtAssetHub},
-	AccountId, Balance, Balances, FellowshipReferenda, GovernanceLocation, Preimage, Runtime,
-	RuntimeCall, RuntimeEvent, RuntimeOrigin, Scheduler, WestendTreasuryAccount, DAYS,
+	xcm_config::{FellowshipAdminBodyId, TreasurerBodyId, UsdtAssetHub},
+	AccountId, AssetRate, Balance, Balances, FellowshipReferenda, GovernanceLocation, Preimage,
+	Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, Scheduler, WestendTreasuryAccount, DAYS,
 };
 use frame_support::{
 	parameter_types,
-	traits::{EitherOf, EitherOfDiverse, MapSuccess, OriginTrait, TryWithMorphedArg},
+	traits::{
+		EitherOf, EitherOfDiverse, MapSuccess, NeverEnsureOrigin, OriginTrait, TryWithMorphedArg,
+	},
+	PalletId,
 };
-use frame_system::EnsureRootWithSuccess;
+use frame_system::{EnsureRoot, EnsureRootWithSuccess};
 pub use origins::{
 	pallet_origins as pallet_fellowship_origins, Architects, EnsureCanPromoteTo, EnsureCanRetainAt,
 	EnsureFellowship, Fellows, Masters, Members, ToVoice,
 };
 use pallet_ranked_collective::EnsureOfRank;
 use pallet_xcm::{EnsureXcm, IsVoiceOfBody};
-use parachains_common::{polkadot::account, HOURS};
+use parachains_common::westend::{account, currency::GRAND};
+use polkadot_runtime_common::impls::{
+	LocatableAssetConverter, VersionedLocatableAsset, VersionedMultiLocationConverter,
+};
+use sp_arithmetic::Permill;
 use sp_core::{ConstU128, ConstU32};
-use sp_runtime::traits::{AccountIdConversion, ConstU16, ConvertToValue, Replace, TakeFirst};
+use sp_runtime::traits::{
+	AccountIdConversion, ConstU16, ConvertToValue, IdentityLookup, Replace, TakeFirst,
+};
+use westend_runtime_constants::time::HOURS;
+use xcm::prelude::*;
 use xcm_builder::{AliasesIntoAccountId32, PayOverXcm};
 
 #[cfg(feature = "runtime-benchmarks")]
 use crate::impls::benchmarks::{OpenHrmpChannel, PayWithEnsure};
+#[cfg(feature = "runtime-benchmarks")]
+use parachains_common::westend::currency::DOLLARS;
 
 /// The Fellowship members' ranks.
 pub mod ranks {
@@ -191,8 +204,6 @@ impl pallet_core_fellowship::Config<FellowshipCoreInstance> for Runtime {
 
 pub type FellowshipSalaryInstance = pallet_salary::Instance1;
 
-use xcm::prelude::*;
-
 parameter_types! {
 	// The interior location on AssetHub for the paying account. This is the Fellowship Salary
 	// pallet instance (which sits at index 64). This sovereign account will need funding.
@@ -236,3 +247,102 @@ impl pallet_salary::Config<FellowshipSalaryInstance> for Runtime {
 	// Total monthly salary budget.
 	type Budget = ConstU128<{ 100_000 * USDT_UNITS }>;
 }
+
+parameter_types! {
+	pub const FellowshipTreasuryPalletId: PalletId = account::FELLOWSHIP_TREASURY_PALLET_ID;
+	pub const HundredPercent: Permill = Permill::from_percent(100);
+	pub const Burn: Permill = Permill::from_percent(0);
+	pub const MaxBalance: Balance = Balance::max_value();
+	// The asset's interior location for the paying account. This is the Fellowship Treasury
+	// pallet instance (which sits at index 65).
+	pub FellowshipTreasuryInteriorLocation: InteriorMultiLocation = PalletInstance(65).into();
+}
+
+#[cfg(feature = "runtime-benchmarks")]
+parameter_types! {
+	// Benchmark bond. Needed to make `propose_spend` work.
+	pub const TenPercent: Permill = Permill::from_percent(10);
+	// Benchmark minimum. Needed to make `propose_spend` work.
+	pub const BenchmarkProposalBondMinimum: Balance = 1 * DOLLARS;
+	// Benchmark maximum. Needed to make `propose_spend` work.
+	pub const BenchmarkProposalBondMaximum: Balance = 10 * DOLLARS;
+}
+
+/// [`PayOverXcm`] setup to pay the Fellowship Treasury.
+pub type FellowshipTreasuryPaymaster = PayOverXcm<
+	FellowshipTreasuryInteriorLocation,
+	crate::xcm_config::XcmRouter,
+	crate::PolkadotXcm,
+	ConstU32<{ 6 * HOURS }>,
+	VersionedMultiLocation,
+	VersionedLocatableAsset,
+	LocatableAssetConverter,
+	VersionedMultiLocationConverter,
+>;
+
+pub type FellowshipTreasuryInstance = pallet_treasury::Instance1;
+
+impl pallet_treasury::Config<FellowshipTreasuryInstance> for Runtime {
+	// The creation of proposals via the treasury pallet is deprecated and should not be utilized.
+	// Instead, public or fellowship referenda should be used to propose and command the treasury
+	// spend or spend_local dispatchables. The parameters below have been configured accordingly to
+	// discourage its use.
+	// TODO: replace with `NeverEnsure` once polkadot-sdk 1.5 is released.
+	type ApproveOrigin = NeverEnsureOrigin<()>;
+	type OnSlash = ();
+	#[cfg(not(feature = "runtime-benchmarks"))]
+	type ProposalBond = HundredPercent;
+	#[cfg(not(feature = "runtime-benchmarks"))]
+	type ProposalBondMinimum = MaxBalance;
+	#[cfg(not(feature = "runtime-benchmarks"))]
+	type ProposalBondMaximum = MaxBalance;
+
+	#[cfg(feature = "runtime-benchmarks")]
+	type ProposalBond = TenPercent;
+	#[cfg(feature = "runtime-benchmarks")]
+	type ProposalBondMinimum = BenchmarkProposalBondMinimum;
+	#[cfg(feature = "runtime-benchmarks")]
+	type ProposalBondMaximum = BenchmarkProposalBondMaximum;
+	// end.
+
+	type WeightInfo = weights::pallet_treasury::WeightInfo<Runtime>;
+	type PalletId = FellowshipTreasuryPalletId;
+	type Currency = Balances;
+	type RejectOrigin = EitherOfDiverse<
+		EnsureRoot<AccountId>,
+		EitherOfDiverse<EnsureXcm<IsVoiceOfBody<GovernanceLocation, TreasurerBodyId>>, Fellows>,
+	>;
+	type RuntimeEvent = RuntimeEvent;
+	type SpendPeriod = ConstU32<{ 7 * DAYS }>;
+	type Burn = Burn;
+	type BurnDestination = ();
+	type SpendFunds = ();
+	type MaxApprovals = ConstU32<100>;
+	type SpendOrigin = EitherOf<
+		EitherOf<
+			EnsureRootWithSuccess<AccountId, MaxBalance>,
+			MapSuccess<
+				EnsureXcm<IsVoiceOfBody<GovernanceLocation, TreasurerBodyId>>,
+				Replace<ConstU128<{ 10_000 * GRAND }>>,
+			>,
+		>,
+		EitherOf<
+			MapSuccess<Architects, Replace<ConstU128<{ 10_000 * GRAND }>>>,
+			MapSuccess<Fellows, Replace<ConstU128<{ 10 * GRAND }>>>,
+		>,
+	>;
+	type AssetKind = VersionedLocatableAsset;
+	type Beneficiary = VersionedMultiLocation;
+	type BeneficiaryLookup = IdentityLookup<Self::Beneficiary>;
+	#[cfg(not(feature = "runtime-benchmarks"))]
+	type Paymaster = FellowshipTreasuryPaymaster;
+	#[cfg(feature = "runtime-benchmarks")]
+	type Paymaster = PayWithEnsure<FellowshipTreasuryPaymaster, OpenHrmpChannel<ConstU32<1000>>>;
+	type BalanceConverter = AssetRate;
+	type PayoutPeriod = ConstU32<{ 30 * DAYS }>;
+	#[cfg(feature = "runtime-benchmarks")]
+	type BenchmarkHelper = polkadot_runtime_common::impls::benchmarks::TreasuryArguments<
+		sp_core::ConstU8<1>,
+		ConstU32<1000>,
+	>;
+}
diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs
index 3b8ce0a8704..9135405155b 100644
--- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs
+++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/lib.rs
@@ -89,14 +89,16 @@ use parachains_common::{
 	SLOT_DURATION,
 };
 use sp_runtime::RuntimeDebug;
-use xcm_config::{GovernanceLocation, XcmOriginToTransactDispatchOrigin};
+use xcm_config::{GovernanceLocation, TreasurerBodyId, XcmOriginToTransactDispatchOrigin};
 
 #[cfg(any(feature = "std", test))]
 pub use sp_runtime::BuildStorage;
 
 // Polkadot imports
 use pallet_xcm::{EnsureXcm, IsVoiceOfBody};
-use polkadot_runtime_common::{BlockHashCount, SlowAdjustingFeeUpdate};
+use polkadot_runtime_common::{
+	impls::VersionedLocatableAsset, BlockHashCount, SlowAdjustingFeeUpdate,
+};
 use xcm::latest::{prelude::*, BodyId};
 
 use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight};
@@ -325,6 +327,7 @@ impl InstanceFilter<RuntimeCall> for ProxyType {
 					RuntimeCall::FellowshipReferenda { .. } |
 					RuntimeCall::FellowshipCore { .. } |
 					RuntimeCall::FellowshipSalary { .. } |
+					RuntimeCall::FellowshipTreasury { .. } |
 					RuntimeCall::Utility { .. } |
 					RuntimeCall::Multisig { .. }
 			),
@@ -613,6 +616,21 @@ impl pallet_preimage::Config for Runtime {
 	>;
 }
 
+impl pallet_asset_rate::Config for Runtime {
+	type WeightInfo = weights::pallet_asset_rate::WeightInfo<Runtime>;
+	type RuntimeEvent = RuntimeEvent;
+	type CreateOrigin = EitherOfDiverse<
+		EnsureRoot<AccountId>,
+		EitherOfDiverse<EnsureXcm<IsVoiceOfBody<GovernanceLocation, TreasurerBodyId>>, Fellows>,
+	>;
+	type RemoveOrigin = Self::CreateOrigin;
+	type UpdateOrigin = Self::CreateOrigin;
+	type Currency = Balances;
+	type AssetKind = VersionedLocatableAsset;
+	#[cfg(feature = "runtime-benchmarks")]
+	type BenchmarkHelper = polkadot_runtime_common::impls::benchmarks::AssetRateArguments;
+}
+
 // Create the runtime by composing the FRAME pallets that were previously configured.
 construct_runtime!(
 	pub enum Runtime
@@ -648,6 +666,7 @@ construct_runtime!(
 		Proxy: pallet_proxy::{Pallet, Call, Storage, Event<T>} = 42,
 		Preimage: pallet_preimage::{Pallet, Call, Storage, Event<T>, HoldReason} = 43,
 		Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event<T>} = 44,
+		AssetRate: pallet_asset_rate::{Pallet, Call, Storage, Event<T>} = 45,
 
 		// The main stage.
 
@@ -665,6 +684,8 @@ construct_runtime!(
 		FellowshipCore: pallet_core_fellowship::<Instance1>::{Pallet, Call, Storage, Event<T>} = 63,
 		// pub type FellowshipSalaryInstance = pallet_salary::Instance1;
 		FellowshipSalary: pallet_salary::<Instance1>::{Pallet, Call, Storage, Event<T>} = 64,
+		// pub type FellowshipTreasuryInstance = pallet_treasury::Instance1;
+		FellowshipTreasury: pallet_treasury::<Instance1>::{Pallet, Call, Storage, Event<T>} = 65,
 
 		// Ambassador Program.
 		AmbassadorCollective: pallet_ranked_collective::<Instance2>::{Pallet, Call, Storage, Event<T>} = 70,
@@ -744,6 +765,8 @@ mod benches {
 		[pallet_collective_content, AmbassadorContent]
 		[pallet_core_fellowship, AmbassadorCore]
 		[pallet_salary, AmbassadorSalary]
+		[pallet_treasury, FellowshipTreasury]
+		[pallet_asset_rate, AssetRate]
 	);
 }
 
diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/mod.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/mod.rs
index d49a2905e7f..77f76342a2e 100644
--- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/mod.rs
+++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/mod.rs
@@ -19,6 +19,7 @@ pub mod cumulus_pallet_xcmp_queue;
 pub mod extrinsic_weights;
 pub mod frame_system;
 pub mod pallet_alliance;
+pub mod pallet_asset_rate;
 pub mod pallet_balances;
 pub mod pallet_collator_selection;
 pub mod pallet_collective;
@@ -38,6 +39,7 @@ pub mod pallet_salary_fellowship_salary;
 pub mod pallet_scheduler;
 pub mod pallet_session;
 pub mod pallet_timestamp;
+pub mod pallet_treasury;
 pub mod pallet_utility;
 pub mod pallet_xcm;
 pub mod paritydb_weights;
diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_asset_rate.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_asset_rate.rs
new file mode 100644
index 00000000000..51b0580f857
--- /dev/null
+++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_asset_rate.rs
@@ -0,0 +1,85 @@
+// Copyright Parity Technologies (UK) Ltd.
+// This file is part of Cumulus.
+
+// Cumulus 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.
+
+// Cumulus 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 Cumulus.  If not, see <http://www.gnu.org/licenses/>.
+
+//! Autogenerated weights for `pallet_asset_rate`
+//!
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
+//! DATE: 2023-11-28, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! WORST CASE MAP SIZE: `1000000`
+//! HOSTNAME: `cob`, CPU: `<UNKNOWN>`
+//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("collectives-westend-dev")`, DB CACHE: 1024
+
+// Executed Command:
+// ./target/debug/polkadot-parachain
+// benchmark
+// pallet
+// --chain=collectives-westend-dev
+// --steps=2
+// --repeat=2
+// --pallet=pallet-asset-rate
+// --extrinsic=*
+// --wasm-execution=compiled
+// --heap-pages=4096
+// --output=./cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/
+
+#![cfg_attr(rustfmt, rustfmt_skip)]
+#![allow(unused_parens)]
+#![allow(unused_imports)]
+#![allow(missing_docs)]
+
+use frame_support::{traits::Get, weights::Weight};
+use core::marker::PhantomData;
+
+/// Weight functions for `pallet_asset_rate`.
+pub struct WeightInfo<T>(PhantomData<T>);
+impl<T: frame_system::Config> pallet_asset_rate::WeightInfo for WeightInfo<T> {
+	/// Storage: `AssetRate::ConversionRateToNative` (r:1 w:1)
+	/// Proof: `AssetRate::ConversionRateToNative` (`max_values`: None, `max_size`: Some(1238), added: 3713, mode: `MaxEncodedLen`)
+	fn create() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `6`
+		//  Estimated: `4703`
+		// Minimum execution time: 102_000_000 picoseconds.
+		Weight::from_parts(112_000_000, 0)
+			.saturating_add(Weight::from_parts(0, 4703))
+			.saturating_add(T::DbWeight::get().reads(1))
+			.saturating_add(T::DbWeight::get().writes(1))
+	}
+	/// Storage: `AssetRate::ConversionRateToNative` (r:1 w:1)
+	/// Proof: `AssetRate::ConversionRateToNative` (`max_values`: None, `max_size`: Some(1238), added: 3713, mode: `MaxEncodedLen`)
+	fn update() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `74`
+		//  Estimated: `4703`
+		// Minimum execution time: 101_000_000 picoseconds.
+		Weight::from_parts(105_000_000, 0)
+			.saturating_add(Weight::from_parts(0, 4703))
+			.saturating_add(T::DbWeight::get().reads(1))
+			.saturating_add(T::DbWeight::get().writes(1))
+	}
+	/// Storage: `AssetRate::ConversionRateToNative` (r:1 w:1)
+	/// Proof: `AssetRate::ConversionRateToNative` (`max_values`: None, `max_size`: Some(1238), added: 3713, mode: `MaxEncodedLen`)
+	fn remove() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `74`
+		//  Estimated: `4703`
+		// Minimum execution time: 112_000_000 picoseconds.
+		Weight::from_parts(116_000_000, 0)
+			.saturating_add(Weight::from_parts(0, 4703))
+			.saturating_add(T::DbWeight::get().reads(1))
+			.saturating_add(T::DbWeight::get().writes(1))
+	}
+}
diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_treasury.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_treasury.rs
new file mode 100644
index 00000000000..58540e646d8
--- /dev/null
+++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/pallet_treasury.rs
@@ -0,0 +1,214 @@
+// Copyright Parity Technologies (UK) Ltd.
+// This file is part of Cumulus.
+
+// Cumulus 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.
+
+// Cumulus 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 Cumulus.  If not, see <http://www.gnu.org/licenses/>.
+
+//! Autogenerated weights for `pallet_treasury`
+//!
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
+//! DATE: 2023-11-28, STEPS: `2`, REPEAT: `2`, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! WORST CASE MAP SIZE: `1000000`
+//! HOSTNAME: `cob`, CPU: `<UNKNOWN>`
+//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("collectives-westend-dev")`, DB CACHE: 1024
+
+// Executed Command:
+// ./target/debug/polkadot-parachain
+// benchmark
+// pallet
+// --chain=collectives-westend-dev
+// --steps=2
+// --repeat=2
+// --pallet=pallet-treasury
+// --extrinsic=*
+// --wasm-execution=compiled
+// --heap-pages=4096
+// --output=./cumulus/parachains/runtimes/collectives/collectives-westend/src/weights/
+
+#![cfg_attr(rustfmt, rustfmt_skip)]
+#![allow(unused_parens)]
+#![allow(unused_imports)]
+#![allow(missing_docs)]
+
+use frame_support::{traits::Get, weights::Weight};
+use core::marker::PhantomData;
+
+/// Weight functions for `pallet_treasury`.
+pub struct WeightInfo<T>(PhantomData<T>);
+impl<T: frame_system::Config> pallet_treasury::WeightInfo for WeightInfo<T> {
+	/// Storage: `FellowshipTreasury::ProposalCount` (r:1 w:1)
+	/// Proof: `FellowshipTreasury::ProposalCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
+	/// Storage: `FellowshipTreasury::Approvals` (r:1 w:1)
+	/// Proof: `FellowshipTreasury::Approvals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`)
+	/// Storage: `FellowshipTreasury::Proposals` (r:0 w:1)
+	/// Proof: `FellowshipTreasury::Proposals` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`)
+	fn spend_local() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `42`
+		//  Estimated: `1887`
+		// Minimum execution time: 117_000_000 picoseconds.
+		Weight::from_parts(126_000_000, 0)
+			.saturating_add(Weight::from_parts(0, 1887))
+			.saturating_add(T::DbWeight::get().reads(2))
+			.saturating_add(T::DbWeight::get().writes(3))
+	}
+	/// Storage: `FellowshipTreasury::ProposalCount` (r:1 w:1)
+	/// Proof: `FellowshipTreasury::ProposalCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
+	/// Storage: `FellowshipTreasury::Proposals` (r:0 w:1)
+	/// Proof: `FellowshipTreasury::Proposals` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`)
+	fn propose_spend() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `143`
+		//  Estimated: `1489`
+		// Minimum execution time: 264_000_000 picoseconds.
+		Weight::from_parts(277_000_000, 0)
+			.saturating_add(Weight::from_parts(0, 1489))
+			.saturating_add(T::DbWeight::get().reads(1))
+			.saturating_add(T::DbWeight::get().writes(2))
+	}
+	/// Storage: `FellowshipTreasury::Proposals` (r:1 w:1)
+	/// Proof: `FellowshipTreasury::Proposals` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`)
+	/// Storage: `System::Account` (r:1 w:1)
+	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
+	fn reject_proposal() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `301`
+		//  Estimated: `3593`
+		// Minimum execution time: 289_000_000 picoseconds.
+		Weight::from_parts(312_000_000, 0)
+			.saturating_add(Weight::from_parts(0, 3593))
+			.saturating_add(T::DbWeight::get().reads(2))
+			.saturating_add(T::DbWeight::get().writes(2))
+	}
+	/// The range of component `p` is `[0, 99]`.
+	fn approve_proposal(_p: u32, ) -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `0`
+		//  Estimated: `0`
+		// Minimum execution time: 0_000 picoseconds.
+		Weight::from_parts(0, 0)
+			.saturating_add(Weight::from_parts(0, 0))
+	}
+	/// Storage: `FellowshipTreasury::Approvals` (r:1 w:1)
+	/// Proof: `FellowshipTreasury::Approvals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`)
+	fn remove_approval() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `127`
+		//  Estimated: `1887`
+		// Minimum execution time: 62_000_000 picoseconds.
+		Weight::from_parts(65_000_000, 0)
+			.saturating_add(Weight::from_parts(0, 1887))
+			.saturating_add(T::DbWeight::get().reads(1))
+			.saturating_add(T::DbWeight::get().writes(1))
+	}
+	/// Storage: `System::Account` (r:199 w:199)
+	/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
+	/// Storage: `FellowshipTreasury::Deactivated` (r:1 w:1)
+	/// Proof: `FellowshipTreasury::Deactivated` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`)
+	/// Storage: `Balances::InactiveIssuance` (r:1 w:1)
+	/// Proof: `Balances::InactiveIssuance` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`)
+	/// Storage: `FellowshipTreasury::Approvals` (r:1 w:1)
+	/// Proof: `FellowshipTreasury::Approvals` (`max_values`: Some(1), `max_size`: Some(402), added: 897, mode: `MaxEncodedLen`)
+	/// Storage: `FellowshipTreasury::Proposals` (r:99 w:99)
+	/// Proof: `FellowshipTreasury::Proposals` (`max_values`: None, `max_size`: Some(108), added: 2583, mode: `MaxEncodedLen`)
+	/// The range of component `p` is `[0, 99]`.
+	fn on_initialize_proposals(p: u32, ) -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `145 + p * (250 ±0)`
+		//  Estimated: `256707 + p * (5206 ±0)`
+		// Minimum execution time: 218_000_000 picoseconds.
+		Weight::from_parts(221_000_000, 0)
+			.saturating_add(Weight::from_parts(0, 256707))
+			// Standard Error: 154_515
+			.saturating_add(Weight::from_parts(399_232_323, 0).saturating_mul(p.into()))
+			.saturating_add(T::DbWeight::get().reads(4))
+			.saturating_add(T::DbWeight::get().reads((3_u64).saturating_mul(p.into())))
+			.saturating_add(T::DbWeight::get().writes(3))
+			.saturating_add(T::DbWeight::get().writes((3_u64).saturating_mul(p.into())))
+			.saturating_add(Weight::from_parts(0, 5206).saturating_mul(p.into()))
+	}
+	/// Storage: `AssetRate::ConversionRateToNative` (r:1 w:0)
+	/// Proof: `AssetRate::ConversionRateToNative` (`max_values`: None, `max_size`: Some(1238), added: 3713, mode: `MaxEncodedLen`)
+	/// Storage: `FellowshipTreasury::SpendCount` (r:1 w:1)
+	/// Proof: `FellowshipTreasury::SpendCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
+	/// Storage: `FellowshipTreasury::Spends` (r:0 w:1)
+	/// Proof: `FellowshipTreasury::Spends` (`max_values`: None, `max_size`: Some(1853), added: 4328, mode: `MaxEncodedLen`)
+	fn spend() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `118`
+		//  Estimated: `4703`
+		// Minimum execution time: 163_000_000 picoseconds.
+		Weight::from_parts(171_000_000, 0)
+			.saturating_add(Weight::from_parts(0, 4703))
+			.saturating_add(T::DbWeight::get().reads(2))
+			.saturating_add(T::DbWeight::get().writes(2))
+	}
+	/// Storage: `FellowshipTreasury::Spends` (r:1 w:1)
+	/// Proof: `FellowshipTreasury::Spends` (`max_values`: None, `max_size`: Some(1853), added: 4328, mode: `MaxEncodedLen`)
+	/// Storage: `ParachainInfo::ParachainId` (r:1 w:0)
+	/// Proof: `ParachainInfo::ParachainId` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
+	/// Storage: `PolkadotXcm::QueryCounter` (r:1 w:1)
+	/// Proof: `PolkadotXcm::QueryCounter` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
+	/// Storage: `XcmpQueue::DeliveryFeeFactor` (r:1 w:0)
+	/// Proof: `XcmpQueue::DeliveryFeeFactor` (`max_values`: None, `max_size`: None, mode: `Measured`)
+	/// Storage: `PolkadotXcm::SupportedVersion` (r:1 w:0)
+	/// Proof: `PolkadotXcm::SupportedVersion` (`max_values`: None, `max_size`: None, mode: `Measured`)
+	/// Storage: `PolkadotXcm::VersionDiscoveryQueue` (r:1 w:1)
+	/// Proof: `PolkadotXcm::VersionDiscoveryQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
+	/// Storage: `PolkadotXcm::SafeXcmVersion` (r:1 w:0)
+	/// Proof: `PolkadotXcm::SafeXcmVersion` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
+	/// Storage: `ParachainSystem::RelevantMessagingState` (r:1 w:0)
+	/// Proof: `ParachainSystem::RelevantMessagingState` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
+	/// Storage: `XcmpQueue::OutboundXcmpStatus` (r:1 w:1)
+	/// Proof: `XcmpQueue::OutboundXcmpStatus` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
+	/// Storage: `XcmpQueue::OutboundXcmpMessages` (r:0 w:1)
+	/// Proof: `XcmpQueue::OutboundXcmpMessages` (`max_values`: None, `max_size`: None, mode: `Measured`)
+	/// Storage: `PolkadotXcm::Queries` (r:0 w:1)
+	/// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`)
+	fn payout() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `629`
+		//  Estimated: `5318`
+		// Minimum execution time: 472_000_000 picoseconds.
+		Weight::from_parts(492_000_000, 0)
+			.saturating_add(Weight::from_parts(0, 5318))
+			.saturating_add(T::DbWeight::get().reads(9))
+			.saturating_add(T::DbWeight::get().writes(6))
+	}
+	/// Storage: `FellowshipTreasury::Spends` (r:1 w:1)
+	/// Proof: `FellowshipTreasury::Spends` (`max_values`: None, `max_size`: Some(1853), added: 4328, mode: `MaxEncodedLen`)
+	/// Storage: `PolkadotXcm::Queries` (r:1 w:1)
+	/// Proof: `PolkadotXcm::Queries` (`max_values`: None, `max_size`: None, mode: `Measured`)
+	fn check_status() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `383`
+		//  Estimated: `5318`
+		// Minimum execution time: 211_000_000 picoseconds.
+		Weight::from_parts(215_000_000, 0)
+			.saturating_add(Weight::from_parts(0, 5318))
+			.saturating_add(T::DbWeight::get().reads(2))
+			.saturating_add(T::DbWeight::get().writes(2))
+	}
+	/// Storage: `FellowshipTreasury::Spends` (r:1 w:1)
+	/// Proof: `FellowshipTreasury::Spends` (`max_values`: None, `max_size`: Some(1853), added: 4328, mode: `MaxEncodedLen`)
+	fn void_spend() -> Weight {
+		// Proof Size summary in bytes:
+		//  Measured:  `179`
+		//  Estimated: `5318`
+		// Minimum execution time: 124_000_000 picoseconds.
+		Weight::from_parts(126_000_000, 0)
+			.saturating_add(Weight::from_parts(0, 5318))
+			.saturating_add(T::DbWeight::get().reads(1))
+			.saturating_add(T::DbWeight::get().writes(1))
+	}
+}
diff --git a/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs
index 44d5d1daf32..9b5b709ab82 100644
--- a/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs
+++ b/cumulus/parachains/runtimes/collectives/collectives-westend/src/xcm_config.rs
@@ -33,6 +33,7 @@ use parachains_common::{
 };
 use polkadot_parachain_primitives::primitives::Sibling;
 use polkadot_runtime_common::xcm_sender::ExponentialPrice;
+use westend_runtime_constants::xcm as xcm_constants;
 use xcm::latest::prelude::*;
 use xcm_builder::{
 	AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses,
@@ -46,8 +47,6 @@ use xcm_builder::{
 };
 use xcm_executor::{traits::WithOriginFilter, XcmExecutor};
 
-const FELLOWSHIP_ADMIN_INDEX: u32 = 1;
-
 parameter_types! {
 	pub const WndLocation: MultiLocation = MultiLocation::parent();
 	pub const RelayNetwork: Option<NetworkId> = Some(NetworkId::Westend);
@@ -57,7 +56,8 @@ parameter_types! {
 	pub RelayTreasuryLocation: MultiLocation = (Parent, PalletInstance(westend_runtime_constants::TREASURY_PALLET_ID)).into();
 	pub CheckingAccount: AccountId = PolkadotXcm::check_account();
 	pub const GovernanceLocation: MultiLocation = MultiLocation::parent();
-	pub const FellowshipAdminBodyId: BodyId = BodyId::Index(FELLOWSHIP_ADMIN_INDEX);
+	pub const FellowshipAdminBodyId: BodyId = BodyId::Index(xcm_constants::body::FELLOWSHIP_ADMIN_INDEX);
+	pub const TreasurerBodyId: BodyId = BodyId::Index(xcm_constants::body::TREASURER_INDEX);
 	pub AssetHub: MultiLocation = (Parent, Parachain(1000)).into();
 	pub AssetHubUsdtId: AssetId = (PalletInstance(50), GeneralIndex(1984)).into();
 	pub UsdtAssetHub: LocatableAssetId = LocatableAssetId {
diff --git a/polkadot/runtime/common/src/impls.rs b/polkadot/runtime/common/src/impls.rs
index 60e631a03ee..d71c626cd98 100644
--- a/polkadot/runtime/common/src/impls.rs
+++ b/polkadot/runtime/common/src/impls.rs
@@ -149,8 +149,11 @@ impl TryConvert<&VersionedMultiLocation, xcm::latest::MultiLocation>
 #[cfg(feature = "runtime-benchmarks")]
 pub mod benchmarks {
 	use super::VersionedLocatableAsset;
+	use core::marker::PhantomData;
+	use frame_support::traits::Get;
 	use pallet_asset_rate::AssetKindFactory;
 	use pallet_treasury::ArgumentsFactory as TreasuryArgumentsFactory;
+	use sp_core::{ConstU32, ConstU8};
 	use xcm::prelude::*;
 
 	/// Provides a factory method for the [`VersionedLocatableAsset`].
@@ -172,12 +175,22 @@ pub mod benchmarks {
 	/// Provide factory methods for the [`VersionedLocatableAsset`] and the `Beneficiary` of the
 	/// [`VersionedMultiLocation`]. The location of the asset is determined as a Parachain with an
 	/// ID equal to the passed seed.
-	pub struct TreasuryArguments;
-	impl TreasuryArgumentsFactory<VersionedLocatableAsset, VersionedMultiLocation>
-		for TreasuryArguments
+	pub struct TreasuryArguments<Parents = ConstU8<0>, ParaId = ConstU32<0>>(
+		PhantomData<(Parents, ParaId)>,
+	);
+	impl<Parents: Get<u8>, ParaId: Get<u32>>
+		TreasuryArgumentsFactory<VersionedLocatableAsset, VersionedMultiLocation>
+		for TreasuryArguments<Parents, ParaId>
 	{
 		fn create_asset_kind(seed: u32) -> VersionedLocatableAsset {
-			AssetRateArguments::create_asset_kind(seed)
+			VersionedLocatableAsset::V3 {
+				location: xcm::v3::MultiLocation::new(Parents::get(), X1(Parachain(ParaId::get()))),
+				asset_id: xcm::v3::MultiLocation::new(
+					0,
+					X2(PalletInstance(seed.try_into().unwrap()), GeneralIndex(seed.into())),
+				)
+				.into(),
+			}
 		}
 		fn create_beneficiary(seed: [u8; 32]) -> VersionedMultiLocation {
 			VersionedMultiLocation::V3(xcm::v3::MultiLocation::new(
diff --git a/polkadot/runtime/westend/constants/src/lib.rs b/polkadot/runtime/westend/constants/src/lib.rs
index de3969afb71..c2bce3a1791 100644
--- a/polkadot/runtime/westend/constants/src/lib.rs
+++ b/polkadot/runtime/westend/constants/src/lib.rs
@@ -124,6 +124,7 @@ pub mod xcm {
 		const ROOT_INDEX: u32 = 0;
 		// The bodies corresponding to the Polkadot OpenGov Origins.
 		pub const FELLOWSHIP_ADMIN_INDEX: u32 = 1;
+		pub const TREASURER_INDEX: u32 = 2;
 	}
 }
 
diff --git a/polkadot/runtime/westend/src/xcm_config.rs b/polkadot/runtime/westend/src/xcm_config.rs
index 9ab6470f6da..d846b982e9a 100644
--- a/polkadot/runtime/westend/src/xcm_config.rs
+++ b/polkadot/runtime/westend/src/xcm_config.rs
@@ -21,7 +21,7 @@ use super::{
 	GeneralAdmin, ParaId, Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, StakingAdmin,
 	TransactionByteFee, Treasury, WeightToFee, XcmPallet,
 };
-
+use crate::governance::pallet_custom_origins::Treasurer;
 use frame_support::{
 	match_types, parameter_types,
 	traits::{Everything, Nothing},
@@ -34,7 +34,9 @@ use runtime_common::{
 };
 use sp_core::ConstU32;
 use westend_runtime_constants::{
-	currency::CENTS, system_parachain::*, xcm::body::FELLOWSHIP_ADMIN_INDEX,
+	currency::CENTS,
+	system_parachain::*,
+	xcm::body::{FELLOWSHIP_ADMIN_INDEX, TREASURER_INDEX},
 };
 use xcm::latest::prelude::*;
 use xcm_builder::{
@@ -198,6 +200,8 @@ parameter_types! {
 	pub const StakingAdminBodyId: BodyId = BodyId::Defense;
 	// FellowshipAdmin pluralistic body.
 	pub const FellowshipAdminBodyId: BodyId = BodyId::Index(FELLOWSHIP_ADMIN_INDEX);
+	// `Treasurer` pluralistic body.
+	pub const TreasurerBodyId: BodyId = BodyId::Index(TREASURER_INDEX);
 }
 
 /// Type to convert the `GeneralAdmin` origin to a Plurality `MultiLocation` value.
@@ -220,6 +224,9 @@ pub type StakingAdminToPlurality =
 pub type FellowshipAdminToPlurality =
 	OriginToPluralityVoice<RuntimeOrigin, FellowshipAdmin, FellowshipAdminBodyId>;
 
+/// Type to convert the `Treasurer` origin to a Plurality `MultiLocation` value.
+pub type TreasurerToPlurality = OriginToPluralityVoice<RuntimeOrigin, Treasurer, TreasurerBodyId>;
+
 /// Type to convert a pallet `Origin` type value into a `MultiLocation` value which represents an
 /// interior location of this chain for a destination chain.
 pub type LocalPalletOriginToLocation = (
@@ -229,6 +236,8 @@ pub type LocalPalletOriginToLocation = (
 	StakingAdminToPlurality,
 	// FellowshipAdmin origin to be used in XCM as a corresponding Plurality `MultiLocation` value.
 	FellowshipAdminToPlurality,
+	// `Treasurer` origin to be used in XCM as a corresponding Plurality `MultiLocation` value.
+	TreasurerToPlurality,
 );
 
 impl pallet_xcm::Config for Runtime {
diff --git a/prdoc/pr_2532.prdoc b/prdoc/pr_2532.prdoc
new file mode 100644
index 00000000000..d0df0ee4aca
--- /dev/null
+++ b/prdoc/pr_2532.prdoc
@@ -0,0 +1,11 @@
+# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
+# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json
+
+title: Westend Fellowship Treasury
+
+doc:
+  - audience: Runtime User
+    description: |
+      Treasury Pallet Instance for the Fellowship in Westend Collectives.
+
+crates: [ ]
diff --git a/substrate/frame/treasury/src/benchmarking.rs b/substrate/frame/treasury/src/benchmarking.rs
index 61fe29dafca..0b9999e37fb 100644
--- a/substrate/frame/treasury/src/benchmarking.rs
+++ b/substrate/frame/treasury/src/benchmarking.rs
@@ -78,8 +78,7 @@ fn create_approved_proposals<T: Config<I>, I: 'static>(n: u32) -> Result<(), &'s
 		#[allow(deprecated)]
 		Treasury::<T, I>::propose_spend(RawOrigin::Signed(caller).into(), value, lookup)?;
 		let proposal_id = <ProposalCount<T, I>>::get() - 1;
-		#[allow(deprecated)]
-		Treasury::<T, I>::approve_proposal(RawOrigin::Root.into(), proposal_id)?;
+		Approvals::<T, I>::try_append(proposal_id).unwrap();
 	}
 	ensure!(<Approvals<T, I>>::get().len() == n as usize, "Not all approved");
 	Ok(())
@@ -163,6 +162,8 @@ mod benchmarks {
 	fn approve_proposal(
 		p: Linear<0, { T::MaxApprovals::get() - 1 }>,
 	) -> Result<(), BenchmarkError> {
+		let approve_origin =
+			T::ApproveOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
 		create_approved_proposals::<T, _>(p)?;
 		let (caller, value, beneficiary_lookup) = setup_proposal::<T, _>(SEED);
 		#[allow(deprecated)]
@@ -172,8 +173,6 @@ mod benchmarks {
 			beneficiary_lookup,
 		)?;
 		let proposal_id = Treasury::<T, _>::proposal_count() - 1;
-		let approve_origin =
-			T::ApproveOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
 
 		#[extrinsic_call]
 		_(approve_origin as T::RuntimeOrigin, proposal_id);
@@ -191,8 +190,7 @@ mod benchmarks {
 			beneficiary_lookup,
 		)?;
 		let proposal_id = Treasury::<T, _>::proposal_count() - 1;
-		#[allow(deprecated)]
-		Treasury::<T, I>::approve_proposal(RawOrigin::Root.into(), proposal_id)?;
+		Approvals::<T, _>::try_append(proposal_id).unwrap();
 		let reject_origin =
 			T::RejectOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
 
-- 
GitLab