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,