diff --git a/parachains/integration-tests/emulated/assets/asset-hub-kusama/Cargo.toml b/parachains/integration-tests/emulated/assets/asset-hub-kusama/Cargo.toml
index a25e2116873..9d2ddf5b6cd 100644
--- a/parachains/integration-tests/emulated/assets/asset-hub-kusama/Cargo.toml
+++ b/parachains/integration-tests/emulated/assets/asset-hub-kusama/Cargo.toml
@@ -16,20 +16,27 @@ sp-core = { default-features = false, git = "https://github.com/paritytech/subst
sp-weights = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-balances = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-assets = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "master" }
+pallet-treasury = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "master" }
+pallet-asset-rate = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "master" }
# Polkadot
polkadot-core-primitives = { default-features = false, git = "https://github.com/paritytech/polkadot", branch = "master" }
polkadot-parachain = { default-features = false, git = "https://github.com/paritytech/polkadot", branch = "master" }
polkadot-runtime-parachains = { git = "https://github.com/paritytech/polkadot", branch = "master" }
polkadot-runtime = { git = "https://github.com/paritytech/polkadot", branch = "master" }
+polkadot-runtime-common = { default-features = false, git = "https://github.com/paritytech/polkadot", branch = "master" }
xcm = { default-features = false, git = "https://github.com/paritytech/polkadot", branch = "master" }
xcm-executor = { default-features = false, git = "https://github.com/paritytech/polkadot", branch = "master" }
+xcm-builder = { default-features = false, git = "https://github.com/paritytech/polkadot", branch = "master" }
pallet-xcm = { default-features = false, git = "https://github.com/paritytech/polkadot", branch = "master" }
# Cumulus
parachains-common = { path = "../../../../common" }
penpal-runtime = { path = "../../../../runtimes/testing/penpal" }
asset-hub-kusama-runtime = { path = "../../../../runtimes/assets/asset-hub-kusama" }
+cumulus-pallet-xcmp-queue = { default-features = false, path = "../../../../../pallets/xcmp-queue" }
+cumulus-pallet-dmp-queue = { default-features = false, path = "../../../../../pallets/dmp-queue" }
+cumulus-pallet-parachain-system = { default-features = false, path = "../../../../../pallets/parachain-system" }
# Local
xcm-emulator = { default-features = false, path = "../../../../../xcm/xcm-emulator" }
diff --git a/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/mod.rs b/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/mod.rs
index 44861d2a872..b370642c35c 100644
--- a/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/mod.rs
+++ b/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/mod.rs
@@ -17,3 +17,4 @@
mod reserve_transfer;
mod teleport;
mod transact;
+mod treasury;
diff --git a/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/treasury.rs b/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/treasury.rs
new file mode 100644
index 00000000000..a74bdfacfd9
--- /dev/null
+++ b/parachains/integration-tests/emulated/assets/asset-hub-kusama/src/tests/treasury.rs
@@ -0,0 +1,123 @@
+// 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 .
+
+use crate::*;
+use frame_support::traits::fungibles::{Create, Inspect, Mutate};
+use polkadot_runtime_common::impls::LocatableAssetId;
+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, PalletInstance(18));
+ // treasury account on a sibling parachain.
+ let treasury_account =
+ asset_hub_kusama_runtime::xcm_config::LocationToAccountId::convert_location(
+ &treasury_location,
+ )
+ .unwrap();
+ let asset_hub_location = MultiLocation::new(0, Parachain(AssetHubKusama::para_id().into()));
+ let root = ::RuntimeOrigin::root();
+ // asset kind to be spend from the treasury.
+ let asset_kind = LocatableAssetId::new(
+ asset_hub_location,
+ AssetId::Concrete((PalletInstance(50), GeneralIndex(ASSET_ID.into())).into()),
+ );
+ // treasury spend beneficiary.
+ let alice: AccountId = Kusama::account_id_of(ALICE);
+ let bob: AccountId = Kusama::account_id_of(BOB);
+ let bob_signed = ::RuntimeOrigin::signed(bob.clone());
+
+ AssetHubKusama::execute_with(|| {
+ type Assets = ::Assets;
+
+ // create an asset class and mint some assets to the treasury account.
+ assert_ok!(>::create(
+ ASSET_ID,
+ treasury_account.clone(),
+ true,
+ SPEND_AMOUNT / 2
+ ));
+ assert_ok!(>::mint_into(ASSET_ID, &treasury_account, SPEND_AMOUNT * 4));
+ // beneficiary has zero balance.
+ assert_eq!(>::balance(ASSET_ID, &alice,), 0u128,);
+ });
+
+ Kusama::execute_with(|| {
+ type RuntimeEvent = ::RuntimeEvent;
+ type Treasury = ::Treasury;
+ type AssetRate = ::AssetRate;
+
+ // create a conversion rate from `asset_kind` to the native currency.
+ assert_ok!(AssetRate::create(root.clone(), asset_kind.clone(), 2.into()));
+
+ // create and approve a treasury spend.
+ assert_ok!(Treasury::spend(
+ root,
+ asset_kind,
+ SPEND_AMOUNT,
+ MultiLocation::new(0, Into::<[u8; 32]>::into(alice.clone())),
+ None,
+ ));
+ // claim the spend.
+ assert_ok!(Treasury::payout(bob_signed.clone(), 0));
+
+ assert_expected_events!(
+ Kusama,
+ vec![
+ RuntimeEvent::Treasury(pallet_treasury::Event::Paid { .. }) => {},
+ ]
+ );
+ });
+
+ AssetHubKusama::execute_with(|| {
+ type RuntimeEvent = ::RuntimeEvent;
+ type Assets = ::Assets;
+
+ // assets transferred, response sent back via UMP.
+ assert_expected_events!(
+ AssetHubKusama,
+ 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::ParachainSystem(cumulus_pallet_parachain_system::Event::UpwardMessageSent { .. }) => {},
+ RuntimeEvent::DmpQueue(cumulus_pallet_dmp_queue::Event::ExecutedDownward { outcome: Outcome::Complete(..) ,.. }) => {},
+ ]
+ );
+ // beneficiary received the assets from the treasury.
+ assert_eq!(>::balance(ASSET_ID, &alice,), SPEND_AMOUNT,);
+ });
+
+ Kusama::execute_with(|| {
+ type RuntimeEvent = ::RuntimeEvent;
+ type Treasury = ::Treasury;
+
+ // check the payment status to ensure the response from the AssetHub was received.
+ assert_ok!(Treasury::check_status(bob_signed, 0));
+ assert_expected_events!(
+ Kusama,
+ vec![
+ RuntimeEvent::Treasury(pallet_treasury::Event::SpendProcessed { .. }) => {},
+ ]
+ );
+ });
+}
diff --git a/parachains/integration-tests/emulated/assets/asset-hub-polkadot/Cargo.toml b/parachains/integration-tests/emulated/assets/asset-hub-polkadot/Cargo.toml
index b518257b1a5..20138338814 100644
--- a/parachains/integration-tests/emulated/assets/asset-hub-polkadot/Cargo.toml
+++ b/parachains/integration-tests/emulated/assets/asset-hub-polkadot/Cargo.toml
@@ -16,6 +16,8 @@ sp-core = { default-features = false, git = "https://github.com/paritytech/subst
sp-weights = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-balances = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "master" }
pallet-assets = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "master" }
+pallet-treasury = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "master" }
+pallet-asset-rate = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "master" }
# Polkadot
polkadot-core-primitives = { default-features = false, git = "https://github.com/paritytech/polkadot", branch = "master" }
@@ -24,12 +26,16 @@ polkadot-runtime-parachains = { git = "https://github.com/paritytech/polkadot",
polkadot-runtime = { git = "https://github.com/paritytech/polkadot", branch = "master" }
xcm = { default-features = false, git = "https://github.com/paritytech/polkadot", branch = "master" }
xcm-executor = { default-features = false, git = "https://github.com/paritytech/polkadot", branch = "master" }
+xcm-builder = { default-features = false, git = "https://github.com/paritytech/polkadot", branch = "master" }
pallet-xcm = { default-features = false, git = "https://github.com/paritytech/polkadot", branch = "master" }
# Cumulus
parachains-common = { path = "../../../../common" }
penpal-runtime = { path = "../../../../runtimes/testing/penpal" }
asset-hub-polkadot-runtime = { path = "../../../../runtimes/assets/asset-hub-polkadot" }
+cumulus-pallet-xcmp-queue = { default-features = false, path = "../../../../../pallets/xcmp-queue" }
+cumulus-pallet-dmp-queue = { default-features = false, path = "../../../../../pallets/dmp-queue" }
+cumulus-pallet-parachain-system = { default-features = false, path = "../../../../../pallets/parachain-system" }
# Local
xcm-emulator = { default-features = false, path = "../../../../../xcm/xcm-emulator" }
diff --git a/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/mod.rs b/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/mod.rs
index 44861d2a872..b370642c35c 100644
--- a/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/mod.rs
+++ b/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/mod.rs
@@ -17,3 +17,4 @@
mod reserve_transfer;
mod teleport;
mod transact;
+mod treasury;
diff --git a/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/treasury.rs b/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/treasury.rs
new file mode 100644
index 00000000000..8060c994b32
--- /dev/null
+++ b/parachains/integration-tests/emulated/assets/asset-hub-polkadot/src/tests/treasury.rs
@@ -0,0 +1,123 @@
+// 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 .
+
+use crate::*;
+use frame_support::traits::fungibles::{Create, Inspect, Mutate};
+use xcm_builder::LocatableAssetId;
+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, PalletInstance(19));
+ // treasury account on a sibling parachain.
+ let treasury_account =
+ asset_hub_polkadot_runtime::xcm_config::LocationToAccountId::convert_location(
+ &treasury_location,
+ )
+ .unwrap();
+ let asset_hub_location = MultiLocation::new(0, Parachain(AssetHubPolkadot::para_id().into()));
+ let root = ::RuntimeOrigin::root();
+ // asset kind to be spent from the treasury.
+ let asset_kind = LocatableAssetId::new(
+ AssetId::Concrete((PalletInstance(50), GeneralIndex(ASSET_ID.into())).into()),
+ asset_hub_location,
+ );
+ // treasury spend beneficiary.
+ let alice: AccountId = Polkadot::account_id_of(ALICE);
+ let bob: AccountId = Polkadot::account_id_of(BOB);
+ let bob_signed = ::RuntimeOrigin::signed(bob.clone());
+
+ AssetHubPolkadot::execute_with(|| {
+ type Assets = ::Assets;
+
+ // create an asset class and mint some assets to the treasury account.
+ assert_ok!(>::create(
+ ASSET_ID,
+ treasury_account.clone(),
+ true,
+ SPEND_AMOUNT / 2
+ ));
+ assert_ok!(>::mint_into(ASSET_ID, &treasury_account, SPEND_AMOUNT * 4));
+ // beneficiary has zero balance.
+ assert_eq!(>::balance(ASSET_ID, &alice,), 0u128,);
+ });
+
+ Polkadot::execute_with(|| {
+ type RuntimeEvent = ::RuntimeEvent;
+ type Treasury = ::Treasury;
+ type AssetRate = ::AssetRate;
+
+ // create a conversion rate from `asset_kind` to the native currency.
+ assert_ok!(AssetRate::create(root.clone(), asset_kind.clone(), 2.into()));
+
+ // create and approve a treasury spend.
+ assert_ok!(Treasury::spend(
+ root,
+ asset_kind,
+ SPEND_AMOUNT,
+ MultiLocation::new(0, Into::<[u8; 32]>::into(alice.clone())),
+ None,
+ ));
+ // claim the spend.
+ assert_ok!(Treasury::payout(bob_signed.clone(), 0));
+
+ assert_expected_events!(
+ Polkadot,
+ vec![
+ RuntimeEvent::Treasury(pallet_treasury::Event::Paid { .. }) => {},
+ ]
+ );
+ });
+
+ AssetHubPolkadot::execute_with(|| {
+ type RuntimeEvent = ::RuntimeEvent;
+ type Assets = ::Assets;
+
+ // assets transferred, response sent back via UMP.
+ assert_expected_events!(
+ AssetHubPolkadot,
+ 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::ParachainSystem(cumulus_pallet_parachain_system::Event::UpwardMessageSent { .. }) => {},
+ RuntimeEvent::DmpQueue(cumulus_pallet_dmp_queue::Event::ExecutedDownward { outcome: Outcome::Complete(..) ,.. }) => {},
+ ]
+ );
+ // beneficiary received the assets from the treasury.
+ assert_eq!(>::balance(ASSET_ID, &alice,), SPEND_AMOUNT,);
+ });
+
+ Polkadot::execute_with(|| {
+ type RuntimeEvent = ::RuntimeEvent;
+ type Treasury = ::Treasury;
+
+ // check the payment status to ensure the response from the AssetHub was received.
+ assert_ok!(Treasury::check_status(bob_signed, 0));
+ assert_expected_events!(
+ Polkadot,
+ vec![
+ RuntimeEvent::Treasury(pallet_treasury::Event::SpendProcessed { .. }) => {},
+ ]
+ );
+ });
+}
diff --git a/parachains/integration-tests/emulated/common/src/lib.rs b/parachains/integration-tests/emulated/common/src/lib.rs
index 23b05a54c72..7d9666a2638 100644
--- a/parachains/integration-tests/emulated/common/src/lib.rs
+++ b/parachains/integration-tests/emulated/common/src/lib.rs
@@ -37,6 +37,8 @@ decl_test_relay_chains! {
},
pallets_extra = {
XcmPallet: polkadot_runtime::XcmPallet,
+ Treasury: polkadot_runtime::Treasury,
+ AssetRate: polkadot_runtime::AssetRate,
}
},
#[api_version(5)]
@@ -56,6 +58,8 @@ decl_test_relay_chains! {
},
pallets_extra = {
XcmPallet: kusama_runtime::XcmPallet,
+ Treasury: kusama_runtime::Treasury,
+ AssetRate: kusama_runtime::AssetRate,
}
},
#[api_version(5)]
diff --git a/parachains/runtimes/assets/asset-hub-kusama/src/xcm_config.rs b/parachains/runtimes/assets/asset-hub-kusama/src/xcm_config.rs
index 2cba64dcb9c..5676b96aa40 100644
--- a/parachains/runtimes/assets/asset-hub-kusama/src/xcm_config.rs
+++ b/parachains/runtimes/assets/asset-hub-kusama/src/xcm_config.rs
@@ -177,6 +177,9 @@ match_types! {
MultiLocation { parents: 1, interior: Here } |
MultiLocation { parents: 1, interior: X1(_) }
};
+ pub type TreasuryPallet: impl Contains = {
+ MultiLocation { parents: 1, interior: X1(PalletInstance(18)) }
+ };
}
/// A call filter for the XCM Transact instruction. This is a temporary measure until we properly
@@ -357,8 +360,9 @@ pub type Barrier = TrailingSetTopicAsId<
(
// If the message is one that immediately attemps to pay for execution, then allow it.
AllowTopLevelPaidExecutionFrom,
- // Parent and its pluralities (i.e. governance bodies) get free execution.
- AllowExplicitUnpaidExecutionFrom,
+ // Parent, parent's pluralities (i.e. governance bodies) and parent's treasury
+ // pallet get free execution.
+ AllowExplicitUnpaidExecutionFrom<(ParentOrParentsPlurality, TreasuryPallet)>,
// Subscriptions for version tracking are OK.
AllowSubscriptionsFrom,
),
diff --git a/parachains/runtimes/assets/asset-hub-polkadot/src/xcm_config.rs b/parachains/runtimes/assets/asset-hub-polkadot/src/xcm_config.rs
index 956ada5ac3b..f3e690e77a9 100644
--- a/parachains/runtimes/assets/asset-hub-polkadot/src/xcm_config.rs
+++ b/parachains/runtimes/assets/asset-hub-polkadot/src/xcm_config.rs
@@ -184,6 +184,9 @@ match_types! {
pub type FellowshipSalaryPallet: impl Contains = {
MultiLocation { parents: 1, interior: X2(Parachain(1001), PalletInstance(64)) }
};
+ pub type TreasuryPallet: impl Contains = {
+ MultiLocation { parents: 1, interior: X1(PalletInstance(19)) }
+ };
}
/// A call filter for the XCM Transact instruction. This is a temporary measure until we properly
@@ -361,11 +364,13 @@ pub type Barrier = DenyThenTry<
(
// If the message is one that immediately attemps to pay for execution, then allow it.
AllowTopLevelPaidExecutionFrom,
- // Parent, its pluralities (i.e. governance bodies), and the Fellows plurality get free execution.
+ // Parent, parent's pluralities (i.e. governance bodies), parent's treasury pallet
+ // and the Fellows plurality get free execution.
AllowExplicitUnpaidExecutionFrom<(
ParentOrParentsPlurality,
FellowsPlurality,
FellowshipSalaryPallet,
+ TreasuryPallet,
)>,
// Subscriptions for version tracking are OK.
AllowSubscriptionsFrom,