Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FRAME: Unity Balance Conversion for Different IDs of Native Asset #3659

Merged
merged 12 commits into from
Apr 16, 2024
35 changes: 35 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 @@ -103,6 +103,7 @@ members = [
"cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-westend",
"cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo",
"cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-westend",
"cumulus/parachains/integration-tests/emulated/tests/collectives/collectives-westend",
"cumulus/parachains/integration-tests/emulated/tests/people/people-rococo",
"cumulus/parachains/integration-tests/emulated/tests/people/people-westend",
"cumulus/parachains/pallets/collective-content",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ decl_test_relay_chains! {
Hrmp: rococo_runtime::Hrmp,
Identity: rococo_runtime::Identity,
IdentityMigrator: rococo_runtime::IdentityMigrator,
Treasury: rococo_runtime::Treasury,
AssetRate: rococo_runtime::AssetRate,
}
},
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,20 @@ pallet-balances = { path = "../../../../../../../substrate/frame/balances", defa
pallet-assets = { path = "../../../../../../../substrate/frame/assets", default-features = false }
pallet-asset-conversion = { path = "../../../../../../../substrate/frame/asset-conversion", default-features = false }
pallet-message-queue = { path = "../../../../../../../substrate/frame/message-queue", default-features = false }
pallet-treasury = { path = "../../../../../../../substrate/frame/treasury", default-features = false }
pallet-utility = { path = "../../../../../../../substrate/frame/utility", default-features = false }

# Polkadot
xcm = { package = "staging-xcm", path = "../../../../../../../polkadot/xcm", default-features = false }
pallet-xcm = { path = "../../../../../../../polkadot/xcm/pallet-xcm", default-features = false }
xcm-executor = { package = "staging-xcm-executor", path = "../../../../../../../polkadot/xcm/xcm-executor", default-features = false }
rococo-runtime = { path = "../../../../../../../polkadot/runtime/rococo" }
polkadot-runtime-common = { path = "../../../../../../../polkadot/runtime/common" }
rococo-runtime-constants = { path = "../../../../../../../polkadot/runtime/rococo/constants" }

# Cumulus
asset-test-utils = { path = "../../../../../runtimes/assets/test-utils" }
cumulus-pallet-parachain-system = { path = "../../../../../../pallets/parachain-system", default-features = false }
parachains-common = { path = "../../../../../common" }
asset-hub-rococo-runtime = { path = "../../../../../runtimes/assets/asset-hub-rococo" }
penpal-runtime = { path = "../../../../../runtimes/testing/penpal" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ mod send;
mod set_xcm_versions;
mod swap;
mod teleport;
mod treasury;
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
// 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::imports::*;
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 parachains_common::AccountId;
use polkadot_runtime_common::impls::VersionedLocatableAsset;
use rococo_runtime::OriginCaller;
use rococo_runtime_constants::currency::GRAND;
use xcm_executor::traits::ConvertLocation;

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

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

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

// Fund Treasury account on Asset Hub with ROCs.

let root = <Rococo 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 `despatch_as` `Signed(treasury_account)`.
assert_ok!(teleport_call.dispatch(root));

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

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

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

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

let alice_location: Location =
[Junction::AccountId32 { network: None, id: Rococo::account_id_of(ALICE).into() }]
.into();
let asset_hub_location: Location = [Parachain(1000)].into();
let native_asset = 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.into(),
Comment on lines +112 to +113
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's weird how the location is relative to the relay chain but the asset id is relative to asset hub. Is this correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, this is correct. DOT is parent on asset hub

}),
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(Rococo::account_id_of(BOB));
assert_ok!(Treasury::payout(bob_signed.clone(), 0));

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

AssetHubRococo::execute_with(|| {
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
type Balances = <AssetHubRococo as AssetHubRococoPallet>::Balances;

// Ensure that the funds deposited to Alice account.

let alice_account = AssetHubRococo::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!(
AssetHubRococo,
vec![
RuntimeEvent::Balances(pallet_balances::Event::Transfer { .. }) => {},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could specify the beneficiary here, so we know the transfer is working

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this assert is broken, I cannot really match beneficiary here. but we are fine here, I check the balance above

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_rococo_runtime::xcm_config::LocationToAccountId::convert_location(
&treasury_location,
)
.unwrap();
let asset_hub_location =
v3::Location::new(0, v3::Junction::Parachain(AssetHubRococo::para_id().into()));
let root = <Rococo 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 = Rococo::account_id_of(ALICE);
let bob: AccountId = Rococo::account_id_of(BOB);
let bob_signed = <Rococo as Chain>::RuntimeOrigin::signed(bob.clone());

AssetHubRococo::execute_with(|| {
type Assets = <AssetHubRococo as AssetHubRococoPallet>::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,);
});

Rococo::execute_with(|| {
type RuntimeEvent = <Rococo as Chain>::RuntimeEvent;
type Treasury = <Rococo as RococoPallet>::Treasury;
type AssetRate = <Rococo as RococoPallet>::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!(
Rococo,
vec![
RuntimeEvent::Treasury(pallet_treasury::Event::Paid { .. }) => {},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably the beneficiary of this can be asserted as well

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does not work right now with this macro

]
);
});

AssetHubRococo::execute_with(|| {
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
type Assets = <AssetHubRococo as AssetHubRococoPallet>::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!(
AssetHubRococo,
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,);
});

Rococo::execute_with(|| {
type RuntimeEvent = <Rococo as Chain>::RuntimeEvent;
type Treasury = <Rococo as RococoPallet>::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!(
Rococo,
vec![
RuntimeEvent::Treasury(pallet_treasury::Event::SpendProcessed { .. }) => {},
]
);
});
}
Loading
Loading