Skip to content

Commit

Permalink
Treasury Spend Detects Relative Locations of Native Asset (#233)
Browse files Browse the repository at this point in the history
Treasury Spend detects the relative locations of native asset to compare
origin's spend limits.

When a spend is approved for a native asset located on Asset Hub, the
AssetRate pallet instance does not recognize the location (parent: 1,
here) as a native asset. Therefore, it requires the asset rate to be set
in advance to the rate of `1`. To address this issue in this PR, we
introduce new types to wrap the `AssetRate` implementation of balance
conversion. These types match the native asset's relative locations and
result in a 1:1 balance conversion in such cases.

In addition we provide integration tests to cover these cases.

Examples:
Treasury pallet on Relay Chain accepts the spend of the native asset on
Asset Hub with it's ID presented as `VersionedLocatableAsset` type
```
{
  `location`: (0, Parachain(1000)),
  `asset_id`: (1, Here),
}
```
and it's balance should be converted 1:1 by treasury pallet's
`BalanceConverter` type.

Note: I plan to introduce a similar but configurable implementation in
the polkadot-sdk and utilize it here. However, to get this change in the
next release, I open this PR first.

Frame version: paritytech/polkadot-sdk#3659

---------

Co-authored-by: joe petrowski <[email protected]>
Co-authored-by: Branislav Kontur <[email protected]>
Co-authored-by: Bastian Köcher <[email protected]>
  • Loading branch information
4 people authored Mar 24, 2024
1 parent b171390 commit 3ae3d29
Show file tree
Hide file tree
Showing 18 changed files with 816 additions and 16 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/integration-tests-matrix.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,9 @@
{
"name": "people-kusama",
"package": "people-kusama-integration-tests"
},
{
"name": "collectives-polkadot",
"package": "collectives-polkadot-integration-tests"
}
]
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Bump parachains runtime API to v10 in Polkadot to enable async-backing subsystems(still in backwards compatible mode) [polkadot-fellows/runtimes#222](https://github.com/polkadot-fellows/runtimes/pull/222)
- Prepared system parachain runtimes for async backing enabling ([polkadot-fellows/runtimes#228](https://github.com/polkadot-fellows/runtimes/pull/228))
- Update runtime weights [polkadot-fellows/runtimes#223](https://github.com/polkadot-fellows/runtimes/pull/223)
- Treasury Spend detects relative locations of the native asset ([polkadot-fellows/runtimes#233](https://github.com/polkadot-fellows/runtimes/pull/233))

### Removed

Expand Down
36 changes: 36 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ members = [
"integration-tests/emulated/tests/assets/asset-hub-polkadot",
"integration-tests/emulated/tests/bridges/bridge-hub-kusama",
"integration-tests/emulated/tests/bridges/bridge-hub-polkadot",
"integration-tests/emulated/tests/collectives/collectives-polkadot",
"integration-tests/emulated/tests/people/people-kusama",
]

Expand Down
2 changes: 2 additions & 0 deletions integration-tests/emulated/chains/relays/kusama/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ decl_test_relay_chains! {
Hrmp: kusama_runtime::Hrmp,
Identity: kusama_runtime::Identity,
IdentityMigrator: kusama_runtime::IdentityMigrator,
Treasury: kusama_runtime::Treasury,
AssetRate: kusama_runtime::AssetRate,
}
},
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@ frame-support = { version = "29.0.0" }
pallet-assets = { version = "30.0.0" }
pallet-balances = { version = "29.0.0" }
pallet-asset-conversion = { version = "11.0.0" }
pallet-treasury = { version = "28.0.0" }
pallet-message-queue = { version = "32.0.0" }
pallet-utility = { version = "29.0.0" }

# Polkadot
xcm = { package = "staging-xcm", version = "8.0.1" }
xcm-executor = { package = "staging-xcm-executor", default-features = false, version = "8.0.1" }
pallet-xcm = { version = "8.0.3" }
runtime-common = { package = "polkadot-runtime-common", default-features = false, version = "8.0.1" }

# Cumulus
parachains-common = { version = "8.0.0" }
Expand All @@ -36,3 +39,4 @@ integration-tests-helpers = { path = "../../../helpers" }
kusama-runtime = { package = "staging-kusama-runtime", path = "../../../../../relay/kusama" }
kusama-system-emulated-network = { path = "../../../networks/kusama-system" }
system-parachains-constants = { path = "../../../../../system-parachains/constants" }
kusama-runtime-constants = { path = "../../../../../relay/kusama/constants" }
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ mod send;
mod set_xcm_versions;
mod swap;
mod teleport;
mod treasury;

use crate::*;
emulated_integration_tests_common::include_penpal_create_foreign_asset_on_asset_hub!(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
// 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.

//! Tests concerning the Kusama Treasury.

use crate::*;
use emulated_integration_tests_common::accounts::{ALICE, BOB};
use frame_support::{
dispatch::RawOrigin,
sp_runtime::traits::Dispatchable,
traits::{
fungible::Inspect,
fungibles::{Create, Inspect as FungiblesInspect, Mutate},
},
};
use kusama_runtime::OriginCaller;
use kusama_runtime_constants::currency::GRAND;
use runtime_common::impls::VersionedLocatableAsset;
use xcm_executor::traits::ConvertLocation;

// Fund Treasury account on Asset Hub from Treasury account on Relay Chain with KSMs.
#[test]
fn spend_ksm_on_asset_hub() {
// initial treasury balance on Asset Hub in KSMs.
let treasury_balance = 9_000 * GRAND;
// the balance spend on Asset Hub.
let treasury_spend_balance = 1_000 * GRAND;

let init_alice_balance = AssetHubKusama::execute_with(|| {
<<AssetHubKusama as AssetHubKusamaPallet>::Balances as Inspect<_>>::balance(
&AssetHubKusama::account_id_of(ALICE),
)
});

Kusama::execute_with(|| {
type RuntimeEvent = <Kusama as Chain>::RuntimeEvent;
type RuntimeCall = <Kusama as Chain>::RuntimeCall;
type Runtime = <Kusama as Chain>::Runtime;
type Balances = <Kusama as KusamaPallet>::Balances;
type Treasury = <Kusama as KusamaPallet>::Treasury;

// Fund Treasury account on Asset Hub with KSMs.

let root = <Kusama as Chain>::RuntimeOrigin::root();
let treasury_account = Treasury::account_id();

// Mint assets to Treasury account on Relay Chain.
assert_ok!(Balances::force_set_balance(
root.clone(),
treasury_account.clone().into(),
treasury_balance * 2,
));

let native_asset = Location::here();
let asset_hub_location: Location = [Parachain(1000)].into();
let treasury_location: Location = (Parent, PalletInstance(18)).into();

let teleport_call = RuntimeCall::Utility(pallet_utility::Call::<Runtime>::dispatch_as {
as_origin: bx!(OriginCaller::system(RawOrigin::Signed(treasury_account))),
call: bx!(RuntimeCall::XcmPallet(pallet_xcm::Call::<Runtime>::teleport_assets {
dest: bx!(VersionedLocation::V4(asset_hub_location.clone())),
beneficiary: bx!(VersionedLocation::V4(treasury_location)),
assets: bx!(VersionedAssets::V4(
Asset { id: native_asset.clone().into(), fun: treasury_balance.into() }.into()
)),
fee_asset_item: 0,
})),
});

// Dispatched from Root to `dispatch_as` `Signed(treasury_account)`.
assert_ok!(teleport_call.dispatch(root));

assert_expected_events!(
Kusama,
vec![
RuntimeEvent::XcmPallet(pallet_xcm::Event::Sent { .. }) => {},
]
);
});

Kusama::execute_with(|| {
type RuntimeEvent = <Kusama as Chain>::RuntimeEvent;
type RuntimeCall = <Kusama as Chain>::RuntimeCall;
type RuntimeOrigin = <Kusama as Chain>::RuntimeOrigin;
type Runtime = <Kusama as Chain>::Runtime;
type Treasury = <Kusama as KusamaPallet>::Treasury;

// Fund Alice account from Kusama Treasury account on Asset Hub.

let treasury_origin: RuntimeOrigin =
kusama_runtime::governance::pallet_custom_origins::Origin::Treasurer.into();

let alice_location: Location =
[Junction::AccountId32 { network: None, id: Kusama::account_id_of(ALICE).into() }]
.into();
let asset_hub_location: Location = [Parachain(1000)].into();
let native_asset_on_asset_hub = Location::parent();

let treasury_spend_call = RuntimeCall::Treasury(pallet_treasury::Call::<Runtime>::spend {
asset_kind: bx!(VersionedLocatableAsset::V4 {
location: asset_hub_location.clone(),
asset_id: native_asset_on_asset_hub.into(),
}),
amount: treasury_spend_balance,
beneficiary: bx!(VersionedLocation::V4(alice_location)),
valid_from: None,
});

assert_ok!(treasury_spend_call.dispatch(treasury_origin));

// Claim the spend.

let bob_signed = RuntimeOrigin::signed(Kusama::account_id_of(BOB));
assert_ok!(Treasury::payout(bob_signed.clone(), 0));

assert_expected_events!(
Kusama,
vec![
RuntimeEvent::Treasury(pallet_treasury::Event::AssetSpendApproved { .. }) => {},
RuntimeEvent::Treasury(pallet_treasury::Event::Paid { .. }) => {},
]
);
});

AssetHubKusama::execute_with(|| {
type RuntimeEvent = <AssetHubKusama as Chain>::RuntimeEvent;
type Balances = <AssetHubKusama as AssetHubKusamaPallet>::Balances;

// Ensure that the funds deposited to Alice account.

let alice_account = AssetHubKusama::account_id_of(ALICE);
assert_eq!(
<Balances as Inspect<_>>::balance(&alice_account),
treasury_spend_balance + init_alice_balance
);

// Assert events triggered by xcm pay program:
// 1. treasury asset transferred to spend beneficiary;
// 2. response to Relay Chain Treasury pallet instance sent back;
// 3. XCM program completed;
assert_expected_events!(
AssetHubKusama,
vec![
RuntimeEvent::Balances(pallet_balances::Event::Transfer { .. }) => {},
RuntimeEvent::ParachainSystem(cumulus_pallet_parachain_system::Event::UpwardMessageSent { .. }) => {},
RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { success: true ,.. }) => {},
]
);
});
}

#[test]
fn create_and_claim_treasury_spend_in_usdt() {
const ASSET_ID: u32 = 1984;
const SPEND_AMOUNT: u128 = 1_000_000;
// treasury location from a sibling parachain.
let treasury_location: Location = Location::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 =
v3::Location::new(0, v3::Junction::Parachain(AssetHubKusama::para_id().into()));
let root = <Kusama as Chain>::RuntimeOrigin::root();
// asset kind to be spend from the treasury.
let asset_kind = VersionedLocatableAsset::V3 {
location: asset_hub_location,
asset_id: v3::AssetId::Concrete(
(v3::Junction::PalletInstance(50), v3::Junction::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 = <Kusama as Chain>::RuntimeOrigin::signed(bob.clone());

AssetHubKusama::execute_with(|| {
type Assets = <AssetHubKusama as AssetHubKusamaPallet>::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 FungiblesInspect<_>>::balance(ASSET_ID, &alice,), 0u128,);
});

Kusama::execute_with(|| {
type RuntimeEvent = <Kusama as Chain>::RuntimeEvent;
type Treasury = <Kusama as KusamaPallet>::Treasury;
type AssetRate = <Kusama as KusamaPallet>::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!(Treasury::spend(
root,
Box::new(asset_kind),
SPEND_AMOUNT,
Box::new(Location::new(0, Into::<[u8; 32]>::into(alice.clone())).into()),
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 = <AssetHubKusama as Chain>::RuntimeEvent;
type Assets = <AssetHubKusama as AssetHubKusamaPallet>::Assets;

// assert events triggered by xcm pay program
// 1. treasury asset transferred to spend beneficiary
// 2. response to Relay Chain treasury pallet instance sent back
// 3. XCM program completed
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::MessageQueue(pallet_message_queue::Event::Processed { success: true ,.. }) => {},
]
);
// beneficiary received the assets from the treasury.
assert_eq!(<Assets as FungiblesInspect<_>>::balance(ASSET_ID, &alice,), SPEND_AMOUNT,);
});

Kusama::execute_with(|| {
type RuntimeEvent = <Kusama as Chain>::RuntimeEvent;
type Treasury = <Kusama as KusamaPallet>::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 { .. }) => {},
]
);
});
}
Loading

0 comments on commit 3ae3d29

Please sign in to comment.