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>
This commit is contained in:
Muharem
2023-12-08 14:02:09 +01:00
committed by GitHub
parent 1bdfb29587
commit da40d97a23
26 changed files with 827 additions and 27 deletions
@@ -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" }
@@ -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,
@@ -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 { .. }) => {},
]
);
});
}
@@ -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;