feat: Rebrand Polkadot/Substrate references to PezkuwiChain

This commit systematically rebrands various references from Parity Technologies'
Polkadot/Substrate ecosystem to PezkuwiChain within the kurdistan-sdk.

Key changes include:
- Updated external repository URLs (zombienet-sdk, parity-db, parity-scale-codec, wasm-instrument) to point to pezkuwichain forks.
- Modified internal documentation and code comments to reflect PezkuwiChain naming and structure.
- Replaced direct references to  with  or specific paths within the  for XCM, Pezkuwi, and other modules.
- Cleaned up deprecated  issue and PR references in various  and  files, particularly in  and  modules.
- Adjusted image and logo URLs in documentation to point to PezkuwiChain assets.
- Removed or rephrased comments related to external Polkadot/Substrate PRs and issues.

This is a significant step towards fully customizing the SDK for the PezkuwiChain ecosystem.
This commit is contained in:
2025-12-14 00:04:10 +03:00
parent 286de54384
commit 1c0e57d984
9084 changed files with 997839 additions and 997557 deletions
@@ -0,0 +1,68 @@
[package]
name = "asset-hub-pezkuwichain-integration-tests"
version = "1.0.0"
authors.workspace = true
edition.workspace = true
license = "Apache-2.0"
description = "Asset Hub Pezkuwichain runtime integration tests with xcm-emulator"
publish = false
[lints]
workspace = true
[dependencies]
assert_matches = { workspace = true }
codec = { workspace = true }
# Bizinikiwi
pezframe-support = { workspace = true }
pezframe-system = { workspace = true }
pezpallet-asset-conversion = { workspace = true }
pezpallet-asset-rewards = { workspace = true }
pezpallet-assets = { workspace = true }
pezpallet-balances = { workspace = true }
pezpallet-message-queue = { workspace = true }
pezpallet-treasury = { workspace = true }
pezpallet-utility = { workspace = true }
pezsp-core = { workspace = true }
pezsp-runtime = { workspace = true }
# Pezkuwi
pezpallet-xcm = { workspace = true }
pezkuwi-runtime-common = { workspace = true, default-features = true }
pezkuwichain-runtime-constants = { workspace = true, default-features = true }
xcm = { workspace = true }
xcm-executor = { workspace = true }
xcm-runtime-apis = { workspace = true, default-features = true }
# Pezcumulus
asset-test-utils = { workspace = true, default-features = true }
pezcumulus-pezpallet-teyrchain-system = { workspace = true }
emulated-integration-tests-common = { workspace = true }
pezkuwichain-system-emulated-network = { workspace = true }
teyrchains-common = { workspace = true, default-features = true }
[features]
runtime-benchmarks = [
"asset-test-utils/runtime-benchmarks",
"pezcumulus-pezpallet-teyrchain-system/runtime-benchmarks",
"emulated-integration-tests-common/runtime-benchmarks",
"pezframe-support/runtime-benchmarks",
"pezframe-system/runtime-benchmarks",
"pezpallet-asset-conversion/runtime-benchmarks",
"pezpallet-asset-rewards/runtime-benchmarks",
"pezpallet-assets/runtime-benchmarks",
"pezpallet-balances/runtime-benchmarks",
"pezpallet-message-queue/runtime-benchmarks",
"pezpallet-treasury/runtime-benchmarks",
"pezpallet-utility/runtime-benchmarks",
"pezpallet-xcm/runtime-benchmarks",
"pezkuwi-runtime-common/runtime-benchmarks",
"pezkuwichain-runtime-constants/runtime-benchmarks",
"pezkuwichain-system-emulated-network/runtime-benchmarks",
"pezsp-runtime/runtime-benchmarks",
"teyrchains-common/runtime-benchmarks",
"xcm-executor/runtime-benchmarks",
"xcm-runtime-apis/runtime-benchmarks",
"xcm/runtime-benchmarks",
]
@@ -0,0 +1,111 @@
// 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.
#[cfg(test)]
mod imports {
pub(crate) use codec::Encode;
// Bizinikiwi
pub(crate) use pezframe_support::{
assert_err, assert_ok,
pezpallet_prelude::Weight,
pezsp_runtime::{DispatchError, DispatchResult, ModuleError},
traits::fungibles::Inspect,
};
// Pezkuwi
pub(crate) use xcm::{
latest::{PEZKUWICHAIN_GENESIS_HASH, ZAGROS_GENESIS_HASH},
prelude::{AccountId32 as AccountId32Junction, *},
};
pub(crate) use xcm_executor::traits::TransferType;
// Pezcumulus
pub(crate) use asset_test_utils::xcm_helpers;
pub(crate) use emulated_integration_tests_common::{
accounts::DUMMY_EMPTY,
test_relay_is_trusted_teleporter, test_teyrchain_is_trusted_teleporter,
test_teyrchain_is_trusted_teleporter_for_relay,
test_xcm_fee_querying_apis_work_for_asset_hub,
xcm_emulator::{
assert_expected_events, bx, Chain, RelayChain as Relay, Test, TestArgs, TestContext,
TestExt, Teyrchain as Para,
},
xcm_helpers::{
fee_asset, get_amount_from_versioned_assets, non_fee_asset, xcm_transact_paid_execution,
},
PenpalATeleportableAssetLocation, ASSETS_PALLET_ID, RESERVABLE_ASSET_ID, XCM_V3,
};
pub(crate) use pezkuwichain_system_emulated_network::{
asset_hub_pezkuwichain_emulated_chain::{
asset_hub_pezkuwichain_runtime::{
self,
xcm_config::{
self as ahr_xcm_config, TokenLocation as RelayLocation, TreasuryAccount,
XcmConfig as AssetHubPezkuwichainXcmConfig,
},
AssetConversionOrigin as AssetHubPezkuwichainAssetConversionOrigin,
ExistentialDeposit as AssetHubPezkuwichainExistentialDeposit,
},
genesis::{AssetHubPezkuwichainAssetOwner, ED as ASSET_HUB_PEZKUWICHAIN_ED},
AssetHubPezkuwichainParaPallet as AssetHubPezkuwichainPallet,
},
penpal_emulated_chain::{
penpal_runtime::xcm_config::{
CustomizableAssetFromSystemAssetHub as PenpalCustomizableAssetFromSystemAssetHub,
LocalReservableFromAssetHub as PenpalLocalReservableFromAssetHub,
LocalTeleportableToAssetHub as PenpalLocalTeleportableToAssetHub,
UsdtFromAssetHub as PenpalUsdtFromAssetHub,
},
PenpalAParaPallet as PenpalAPallet, PenpalAssetOwner,
PenpalBParaPallet as PenpalBPallet, ED as PENPAL_ED,
},
pezkuwichain_emulated_chain::{
genesis::ED as PEZKUWICHAIN_ED,
pezkuwichain_runtime::{
governance as pezkuwichain_governance,
governance::pezpallet_custom_origins::Origin::Treasurer,
xcm_config::UniversalLocation as PezkuwichainUniversalLocation, Dmp,
OriginCaller as PezkuwichainOriginCaller,
},
PezkuwichainRelayPallet as PezkuwichainPallet,
},
AssetHubPezkuwichainPara as AssetHubPezkuwichain,
AssetHubPezkuwichainParaReceiver as AssetHubPezkuwichainReceiver,
AssetHubPezkuwichainParaSender as AssetHubPezkuwichainSender,
BridgeHubPezkuwichainPara as BridgeHubPezkuwichain,
BridgeHubPezkuwichainParaReceiver as BridgeHubPezkuwichainReceiver, PenpalAPara as PenpalA,
PenpalAParaReceiver as PenpalAReceiver, PenpalAParaSender as PenpalASender,
PenpalBPara as PenpalB, PenpalBParaReceiver as PenpalBReceiver,
PezkuwichainRelay as Pezkuwichain, PezkuwichainRelayReceiver as PezkuwichainReceiver,
PezkuwichainRelaySender as PezkuwichainSender,
};
pub(crate) use teyrchains_common::Balance;
pub(crate) const ASSET_ID: u32 = 3;
pub(crate) const ASSET_MIN_BALANCE: u128 = 1000;
pub(crate) type RelayToParaTest = Test<Pezkuwichain, PenpalA>;
pub(crate) type ParaToRelayTest = Test<PenpalA, Pezkuwichain>;
pub(crate) type SystemParaToRelayTest = Test<AssetHubPezkuwichain, Pezkuwichain>;
pub(crate) type SystemParaToParaTest = Test<AssetHubPezkuwichain, PenpalA>;
pub(crate) type ParaToSystemParaTest = Test<PenpalA, AssetHubPezkuwichain>;
pub(crate) type ParaToParaThroughRelayTest = Test<PenpalA, PenpalB, Pezkuwichain>;
pub(crate) type ParaToParaThroughAHTest = Test<PenpalA, PenpalB, AssetHubPezkuwichain>;
pub(crate) type RelayToParaThroughAHTest = Test<Pezkuwichain, PenpalA, AssetHubPezkuwichain>;
}
#[cfg(test)]
mod tests;
@@ -0,0 +1,34 @@
// 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 related to claiming assets trapped during XCM execution.
use crate::imports::*;
use emulated_integration_tests_common::test_chain_can_claim_assets;
#[test]
fn assets_can_be_claimed() {
let amount = AssetHubPezkuwichainExistentialDeposit::get();
let assets: Assets = (Parent, amount).into();
test_chain_can_claim_assets!(
AssetHubPezkuwichain,
RuntimeCall,
NetworkId::ByGenesis(PEZKUWICHAIN_GENESIS_HASH),
assets,
amount
);
}
@@ -0,0 +1,868 @@
// 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 super::reserve_transfer::*;
use crate::{
imports::*,
tests::teleport::do_bidirectional_teleport_foreign_assets_between_para_and_asset_hub_using_xt,
};
fn para_to_para_assethub_hop_assertions(t: ParaToParaThroughAHTest) {
type RuntimeEvent = <AssetHubPezkuwichain as Chain>::RuntimeEvent;
let sov_penpal_a_on_ah = AssetHubPezkuwichain::sovereign_account_id_of(
AssetHubPezkuwichain::sibling_location_of(PenpalA::para_id()),
);
let sov_penpal_b_on_ah = AssetHubPezkuwichain::sovereign_account_id_of(
AssetHubPezkuwichain::sibling_location_of(PenpalB::para_id()),
);
assert_expected_events!(
AssetHubPezkuwichain,
vec![
// Withdrawn from sender teyrchain SA
RuntimeEvent::Balances(
pezpallet_balances::Event::Burned { who, amount }
) => {
who: *who == sov_penpal_a_on_ah,
amount: *amount == t.args.amount,
},
// Deposited to receiver teyrchain SA
RuntimeEvent::Balances(
pezpallet_balances::Event::Minted { who, .. }
) => {
who: *who == sov_penpal_b_on_ah,
},
RuntimeEvent::MessageQueue(
pezpallet_message_queue::Event::Processed { success: true, .. }
) => {},
]
);
}
fn ah_to_para_transfer_assets(t: SystemParaToParaTest) -> DispatchResult {
let fee: Asset = t
.args
.assets
.inner()
.iter()
.find(|a| a.id == t.args.fee_asset_id)
.cloned()
.unwrap();
let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset {
assets: Wild(AllCounted(t.args.assets.len() as u32)),
beneficiary: t.args.beneficiary,
}]);
<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::PezkuwiXcm::transfer_assets_using_type_and_then(
t.signed_origin,
bx!(t.args.dest.into()),
bx!(t.args.assets.into()),
bx!(TransferType::LocalReserve),
bx!(fee.id.into()),
bx!(TransferType::LocalReserve),
bx!(VersionedXcm::from(custom_xcm_on_dest)),
t.args.weight_limit,
)
}
fn para_to_ah_transfer_assets(t: ParaToSystemParaTest) -> DispatchResult {
let fee: Asset = t
.args
.assets
.inner()
.iter()
.find(|a| a.id == t.args.fee_asset_id)
.cloned()
.unwrap();
let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset {
assets: Wild(AllCounted(t.args.assets.len() as u32)),
beneficiary: t.args.beneficiary,
}]);
<PenpalA as PenpalAPallet>::PezkuwiXcm::transfer_assets_using_type_and_then(
t.signed_origin,
bx!(t.args.dest.into()),
bx!(t.args.assets.into()),
bx!(TransferType::DestinationReserve),
bx!(fee.id.into()),
bx!(TransferType::DestinationReserve),
bx!(VersionedXcm::from(custom_xcm_on_dest)),
t.args.weight_limit,
)
}
fn para_to_para_transfer_assets_through_ah(t: ParaToParaThroughAHTest) -> DispatchResult {
let fee: Asset = t
.args
.assets
.inner()
.iter()
.find(|a| a.id == t.args.fee_asset_id)
.cloned()
.unwrap();
let asset_hub_location: Location =
PenpalA::sibling_location_of(AssetHubPezkuwichain::para_id());
let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset {
assets: Wild(AllCounted(t.args.assets.len() as u32)),
beneficiary: t.args.beneficiary,
}]);
<PenpalA as PenpalAPallet>::PezkuwiXcm::transfer_assets_using_type_and_then(
t.signed_origin,
bx!(t.args.dest.into()),
bx!(t.args.assets.into()),
bx!(TransferType::RemoteReserve(asset_hub_location.clone().into())),
bx!(fee.id.into()),
bx!(TransferType::RemoteReserve(asset_hub_location.into())),
bx!(VersionedXcm::from(custom_xcm_on_dest)),
t.args.weight_limit,
)
}
fn para_to_asset_hub_teleport_foreign_assets(t: ParaToSystemParaTest) -> DispatchResult {
let fee: Asset = t
.args
.assets
.inner()
.iter()
.find(|a| a.id == t.args.fee_asset_id)
.cloned()
.unwrap();
let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset {
assets: Wild(AllCounted(t.args.assets.len() as u32)),
beneficiary: t.args.beneficiary,
}]);
<PenpalA as PenpalAPallet>::PezkuwiXcm::transfer_assets_using_type_and_then(
t.signed_origin,
bx!(t.args.dest.into()),
bx!(t.args.assets.into()),
bx!(TransferType::Teleport),
bx!(fee.id.into()),
bx!(TransferType::DestinationReserve),
bx!(VersionedXcm::from(custom_xcm_on_dest)),
t.args.weight_limit,
)
}
fn asset_hub_to_para_teleport_foreign_assets(t: SystemParaToParaTest) -> DispatchResult {
let fee: Asset = t
.args
.assets
.inner()
.iter()
.find(|a| a.id == t.args.fee_asset_id)
.cloned()
.unwrap();
let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset {
assets: Wild(AllCounted(t.args.assets.len() as u32)),
beneficiary: t.args.beneficiary,
}]);
<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::PezkuwiXcm::transfer_assets_using_type_and_then(
t.signed_origin,
bx!(t.args.dest.into()),
bx!(t.args.assets.into()),
bx!(TransferType::Teleport),
bx!(fee.id.into()),
bx!(TransferType::LocalReserve),
bx!(VersionedXcm::from(custom_xcm_on_dest)),
t.args.weight_limit,
)
}
// ===========================================================================
// ======= Transfer - Native + Bridged Assets - AssetHub->Teyrchain ==========
// ===========================================================================
/// Transfers of native asset plus bridged asset from AssetHub to some Teyrchain
/// while paying fees using native asset.
#[test]
fn transfer_foreign_assets_from_asset_hub_to_para() {
let destination = AssetHubPezkuwichain::sibling_location_of(PenpalA::para_id());
let sender = AssetHubPezkuwichainSender::get();
let native_amount_to_send: Balance = ASSET_HUB_PEZKUWICHAIN_ED * 10000;
let native_asset_location = RelayLocation::get();
let receiver = PenpalAReceiver::get();
let assets_owner = PenpalAssetOwner::get();
// Foreign asset used: bridged ZGR
let foreign_amount_to_send = ASSET_HUB_PEZKUWICHAIN_ED * 10_000_000;
let wnd_at_pezkuwichain_teyrchains =
Location::new(2, [Junction::GlobalConsensus(NetworkId::ByGenesis(ZAGROS_GENESIS_HASH))]);
// Configure destination chain to trust AH as reserve of ZGR
PenpalA::execute_with(|| {
assert_ok!(<PenpalA as Chain>::System::set_storage(
<PenpalA as Chain>::RuntimeOrigin::root(),
vec![(
PenpalCustomizableAssetFromSystemAssetHub::key().to_vec(),
Location::new(2, [GlobalConsensus(ByGenesis(ZAGROS_GENESIS_HASH))]).encode(),
)],
));
});
PenpalA::force_create_foreign_asset(
wnd_at_pezkuwichain_teyrchains.clone(),
assets_owner.clone(),
false,
ASSET_MIN_BALANCE,
vec![],
);
AssetHubPezkuwichain::force_create_foreign_asset(
wnd_at_pezkuwichain_teyrchains.clone().try_into().unwrap(),
assets_owner.clone(),
false,
ASSET_MIN_BALANCE,
vec![],
);
AssetHubPezkuwichain::mint_foreign_asset(
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(assets_owner),
wnd_at_pezkuwichain_teyrchains.clone().try_into().unwrap(),
sender.clone(),
foreign_amount_to_send * 2,
);
// Assets to send
let assets: Vec<Asset> = vec![
(Parent, native_amount_to_send).into(),
(wnd_at_pezkuwichain_teyrchains.clone(), foreign_amount_to_send).into(),
];
let fee_asset_id = AssetId(Parent.into());
// Init Test
let test_args = TestContext {
sender: sender.clone(),
receiver: receiver.clone(),
args: TestArgs::new_para(
destination.clone(),
receiver.clone(),
native_amount_to_send,
assets.into(),
None,
fee_asset_id,
),
};
let mut test = SystemParaToParaTest::new(test_args);
// Query initial balances
let sender_balance_before = test.sender.balance;
let sender_wnds_before = AssetHubPezkuwichain::execute_with(|| {
type ForeignAssets = <AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(
wnd_at_pezkuwichain_teyrchains.clone().try_into().unwrap(),
&sender,
)
});
let receiver_assets_before = PenpalA::execute_with(|| {
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(native_asset_location.clone(), &receiver)
});
let receiver_wnds_before = PenpalA::execute_with(|| {
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(wnd_at_pezkuwichain_teyrchains.clone(), &receiver)
});
// Set assertions and dispatchables
test.set_assertion::<AssetHubPezkuwichain>(system_para_to_para_sender_assertions);
test.set_assertion::<PenpalA>(system_para_to_para_receiver_assertions);
test.set_dispatchable::<AssetHubPezkuwichain>(ah_to_para_transfer_assets);
test.assert();
// Query final balances
let sender_balance_after = test.sender.balance;
let sender_wnds_after = AssetHubPezkuwichain::execute_with(|| {
type ForeignAssets = <AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(
wnd_at_pezkuwichain_teyrchains.clone().try_into().unwrap(),
&sender,
)
});
let receiver_assets_after = PenpalA::execute_with(|| {
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(native_asset_location, &receiver)
});
let receiver_wnds_after = PenpalA::execute_with(|| {
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(wnd_at_pezkuwichain_teyrchains, &receiver)
});
// Sender's balance is reduced by amount sent plus delivery fees
assert!(sender_balance_after < sender_balance_before - native_amount_to_send);
// Sender's balance is reduced by foreign amount sent
assert_eq!(sender_wnds_after, sender_wnds_before - foreign_amount_to_send);
// Receiver's assets is increased
assert!(receiver_assets_after > receiver_assets_before);
// Receiver's assets increased by `amount_to_send - delivery_fees - bought_execution`;
// `delivery_fees` might be paid from transfer or JIT, also `bought_execution` is unknown but
// should be non-zero
assert!(receiver_assets_after < receiver_assets_before + native_amount_to_send);
// Receiver's balance is increased by foreign amount sent
assert_eq!(receiver_wnds_after, receiver_wnds_before + foreign_amount_to_send);
}
/// Reserve Transfers of native asset from Teyrchain to System Teyrchain should work
// ===========================================================================
// ======= Transfer - Native + Bridged Assets - Teyrchain->AssetHub ==========
// ===========================================================================
/// Transfers of native asset plus bridged asset from some Teyrchain to AssetHub
/// while paying fees using native asset.
#[test]
fn transfer_foreign_assets_from_para_to_asset_hub() {
// Init values for Teyrchain
let destination = PenpalA::sibling_location_of(AssetHubPezkuwichain::para_id());
let sender = PenpalASender::get();
let native_amount_to_send: Balance = ASSET_HUB_PEZKUWICHAIN_ED * 10000;
let native_asset_location = RelayLocation::get();
let assets_owner = PenpalAssetOwner::get();
// Foreign asset used: bridged ZGR
let foreign_amount_to_send = ASSET_HUB_PEZKUWICHAIN_ED * 10_000_000;
let wnd_at_pezkuwichain_teyrchains =
Location::new(2, [Junction::GlobalConsensus(NetworkId::ByGenesis(ZAGROS_GENESIS_HASH))]);
// Configure destination chain to trust AH as reserve of ZGR
PenpalA::execute_with(|| {
assert_ok!(<PenpalA as Chain>::System::set_storage(
<PenpalA as Chain>::RuntimeOrigin::root(),
vec![(
PenpalCustomizableAssetFromSystemAssetHub::key().to_vec(),
Location::new(2, [GlobalConsensus(ByGenesis(ZAGROS_GENESIS_HASH))]).encode(),
)],
));
});
PenpalA::force_create_foreign_asset(
wnd_at_pezkuwichain_teyrchains.clone(),
assets_owner.clone(),
false,
ASSET_MIN_BALANCE,
vec![],
);
AssetHubPezkuwichain::force_create_foreign_asset(
wnd_at_pezkuwichain_teyrchains.clone().try_into().unwrap(),
assets_owner.clone(),
false,
ASSET_MIN_BALANCE,
vec![],
);
// fund Teyrchain's sender account
PenpalA::mint_foreign_asset(
<PenpalA as Chain>::RuntimeOrigin::signed(assets_owner.clone()),
native_asset_location.clone(),
sender.clone(),
native_amount_to_send * 2,
);
PenpalA::mint_foreign_asset(
<PenpalA as Chain>::RuntimeOrigin::signed(assets_owner.clone()),
wnd_at_pezkuwichain_teyrchains.clone(),
sender.clone(),
foreign_amount_to_send * 2,
);
// Init values for System Teyrchain
let receiver = AssetHubPezkuwichainReceiver::get();
let penpal_location_as_seen_by_ahr =
AssetHubPezkuwichain::sibling_location_of(PenpalA::para_id());
let sov_penpal_on_ahr =
AssetHubPezkuwichain::sovereign_account_id_of(penpal_location_as_seen_by_ahr);
// fund Teyrchain's SA on AssetHub with the assets held in reserve
AssetHubPezkuwichain::fund_accounts(vec![(
sov_penpal_on_ahr.clone().into(),
native_amount_to_send * 2,
)]);
AssetHubPezkuwichain::mint_foreign_asset(
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(assets_owner),
wnd_at_pezkuwichain_teyrchains.clone().try_into().unwrap(),
sov_penpal_on_ahr,
foreign_amount_to_send * 2,
);
// Assets to send
let assets: Vec<Asset> = vec![
(Parent, native_amount_to_send).into(),
(wnd_at_pezkuwichain_teyrchains.clone(), foreign_amount_to_send).into(),
];
let fee_asset_id = AssetId(Parent.into());
// Init Test
let test_args = TestContext {
sender: sender.clone(),
receiver: receiver.clone(),
args: TestArgs::new_para(
destination.clone(),
receiver.clone(),
native_amount_to_send,
assets.into(),
None,
fee_asset_id,
),
};
let mut test = ParaToSystemParaTest::new(test_args);
// Query initial balances
let sender_native_before = PenpalA::execute_with(|| {
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(native_asset_location.clone(), &sender)
});
let sender_wnds_before = PenpalA::execute_with(|| {
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(wnd_at_pezkuwichain_teyrchains.clone(), &sender)
});
let receiver_native_before = test.receiver.balance;
let receiver_wnds_before = AssetHubPezkuwichain::execute_with(|| {
type ForeignAssets = <AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(
wnd_at_pezkuwichain_teyrchains.clone().try_into().unwrap(),
&receiver,
)
});
// Set assertions and dispatchables
test.set_assertion::<PenpalA>(para_to_system_para_sender_assertions);
test.set_assertion::<AssetHubPezkuwichain>(para_to_system_para_receiver_assertions);
test.set_dispatchable::<PenpalA>(para_to_ah_transfer_assets);
test.assert();
// Query final balances
let sender_native_after = PenpalA::execute_with(|| {
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(native_asset_location, &sender)
});
let sender_wnds_after = PenpalA::execute_with(|| {
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(wnd_at_pezkuwichain_teyrchains.clone(), &sender)
});
let receiver_native_after = test.receiver.balance;
let receiver_wnds_after = AssetHubPezkuwichain::execute_with(|| {
type ForeignAssets = <AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(
wnd_at_pezkuwichain_teyrchains.try_into().unwrap(),
&receiver,
)
});
// Sender's balance is reduced by amount sent plus delivery fees
assert!(sender_native_after < sender_native_before - native_amount_to_send);
// Sender's balance is reduced by foreign amount sent
assert_eq!(sender_wnds_after, sender_wnds_before - foreign_amount_to_send);
// Receiver's balance is increased
assert!(receiver_native_after > receiver_native_before);
// Receiver's balance increased by `amount_to_send - delivery_fees - bought_execution`;
// `delivery_fees` might be paid from transfer or JIT, also `bought_execution` is unknown but
// should be non-zero
assert!(receiver_native_after < receiver_native_before + native_amount_to_send);
// Receiver's balance is increased by foreign amount sent
assert_eq!(receiver_wnds_after, receiver_wnds_before + foreign_amount_to_send);
}
// ==============================================================================
// ===== Transfer - Native + Bridged Assets - Teyrchain->AssetHub->Teyrchain ====
// ==============================================================================
/// Transfers of native asset plus bridged asset from Teyrchain to Teyrchain
/// (through AssetHub reserve) with fees paid using native asset.
#[test]
fn transfer_foreign_assets_from_para_to_para_through_asset_hub() {
// Init values for Teyrchain Origin
let destination = PenpalA::sibling_location_of(PenpalB::para_id());
let sender = PenpalASender::get();
let roc_to_send: Balance = PEZKUWICHAIN_ED * 10000;
let assets_owner = PenpalAssetOwner::get();
let roc_location = RelayLocation::get();
let sender_as_seen_by_ah = AssetHubPezkuwichain::sibling_location_of(PenpalA::para_id());
let sov_of_sender_on_ah = AssetHubPezkuwichain::sovereign_account_id_of(sender_as_seen_by_ah);
let receiver_as_seen_by_ah = AssetHubPezkuwichain::sibling_location_of(PenpalB::para_id());
let sov_of_receiver_on_ah =
AssetHubPezkuwichain::sovereign_account_id_of(receiver_as_seen_by_ah);
let wnd_to_send = ASSET_HUB_PEZKUWICHAIN_ED * 10_000_000;
// Configure source and destination chains to trust AH as reserve of ZGR
PenpalA::execute_with(|| {
assert_ok!(<PenpalA as Chain>::System::set_storage(
<PenpalA as Chain>::RuntimeOrigin::root(),
vec![(
PenpalCustomizableAssetFromSystemAssetHub::key().to_vec(),
Location::new(2, [GlobalConsensus(ByGenesis(ZAGROS_GENESIS_HASH))]).encode(),
)],
));
});
PenpalB::execute_with(|| {
assert_ok!(<PenpalB as Chain>::System::set_storage(
<PenpalB as Chain>::RuntimeOrigin::root(),
vec![(
PenpalCustomizableAssetFromSystemAssetHub::key().to_vec(),
Location::new(2, [GlobalConsensus(ByGenesis(ZAGROS_GENESIS_HASH))]).encode(),
)],
));
});
// Register ZGR as foreign asset and transfer it around the Pezkuwichain ecosystem
let wnd_at_pezkuwichain_teyrchains =
Location::new(2, [Junction::GlobalConsensus(NetworkId::ByGenesis(ZAGROS_GENESIS_HASH))]);
AssetHubPezkuwichain::force_create_foreign_asset(
wnd_at_pezkuwichain_teyrchains.clone().try_into().unwrap(),
assets_owner.clone(),
false,
ASSET_MIN_BALANCE,
vec![],
);
PenpalA::force_create_foreign_asset(
wnd_at_pezkuwichain_teyrchains.clone(),
assets_owner.clone(),
false,
ASSET_MIN_BALANCE,
vec![],
);
PenpalB::force_create_foreign_asset(
wnd_at_pezkuwichain_teyrchains.clone(),
assets_owner.clone(),
false,
ASSET_MIN_BALANCE,
vec![],
);
// fund Teyrchain's sender account
PenpalA::mint_foreign_asset(
<PenpalA as Chain>::RuntimeOrigin::signed(assets_owner.clone()),
roc_location.clone(),
sender.clone(),
roc_to_send * 2,
);
PenpalA::mint_foreign_asset(
<PenpalA as Chain>::RuntimeOrigin::signed(assets_owner.clone()),
wnd_at_pezkuwichain_teyrchains.clone(),
sender.clone(),
wnd_to_send * 2,
);
// fund the Teyrchain Origin's SA on Asset Hub with the assets held in reserve
AssetHubPezkuwichain::fund_accounts(vec![(
sov_of_sender_on_ah.clone().into(),
roc_to_send * 2,
)]);
AssetHubPezkuwichain::mint_foreign_asset(
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(assets_owner),
wnd_at_pezkuwichain_teyrchains.clone().try_into().unwrap(),
sov_of_sender_on_ah.clone(),
wnd_to_send * 2,
);
// Init values for Teyrchain Destination
let receiver = PenpalBReceiver::get();
// Assets to send
let assets: Vec<Asset> = vec![
(roc_location.clone(), roc_to_send).into(),
(wnd_at_pezkuwichain_teyrchains.clone(), wnd_to_send).into(),
];
let fee_asset_id: AssetId = roc_location.clone().into();
// Init Test
let test_args = TestContext {
sender: sender.clone(),
receiver: receiver.clone(),
args: TestArgs::new_para(
destination,
receiver.clone(),
roc_to_send,
assets.into(),
None,
fee_asset_id,
),
};
let mut test = ParaToParaThroughAHTest::new(test_args);
// Query initial balances
let sender_rocs_before = PenpalA::execute_with(|| {
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(roc_location.clone(), &sender)
});
let sender_wnds_before = PenpalA::execute_with(|| {
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(wnd_at_pezkuwichain_teyrchains.clone(), &sender)
});
let rocs_in_sender_reserve_on_ahr_before =
<AssetHubPezkuwichain as Chain>::account_data_of(sov_of_sender_on_ah.clone()).free;
let wnds_in_sender_reserve_on_ahr_before = AssetHubPezkuwichain::execute_with(|| {
type Assets = <AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::ForeignAssets;
<Assets as Inspect<_>>::balance(
wnd_at_pezkuwichain_teyrchains.clone().try_into().unwrap(),
&sov_of_sender_on_ah,
)
});
let rocs_in_receiver_reserve_on_ahr_before =
<AssetHubPezkuwichain as Chain>::account_data_of(sov_of_receiver_on_ah.clone()).free;
let wnds_in_receiver_reserve_on_ahr_before = AssetHubPezkuwichain::execute_with(|| {
type Assets = <AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::ForeignAssets;
<Assets as Inspect<_>>::balance(
wnd_at_pezkuwichain_teyrchains.clone().try_into().unwrap(),
&sov_of_receiver_on_ah,
)
});
let receiver_rocs_before = PenpalB::execute_with(|| {
type ForeignAssets = <PenpalB as PenpalBPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(roc_location.clone(), &receiver)
});
let receiver_wnds_before = PenpalB::execute_with(|| {
type ForeignAssets = <PenpalB as PenpalBPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(wnd_at_pezkuwichain_teyrchains.clone(), &receiver)
});
// Set assertions and dispatchables
test.set_assertion::<PenpalA>(para_to_para_through_hop_sender_assertions);
test.set_assertion::<AssetHubPezkuwichain>(para_to_para_assethub_hop_assertions);
test.set_assertion::<PenpalB>(para_to_para_through_hop_receiver_assertions);
test.set_dispatchable::<PenpalA>(para_to_para_transfer_assets_through_ah);
test.assert();
// Query final balances
let sender_rocs_after = PenpalA::execute_with(|| {
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(roc_location.clone(), &sender)
});
let sender_wnds_after = PenpalA::execute_with(|| {
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(wnd_at_pezkuwichain_teyrchains.clone(), &sender)
});
let wnds_in_sender_reserve_on_ahr_after = AssetHubPezkuwichain::execute_with(|| {
type Assets = <AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::ForeignAssets;
<Assets as Inspect<_>>::balance(
wnd_at_pezkuwichain_teyrchains.clone().try_into().unwrap(),
&sov_of_sender_on_ah,
)
});
let rocs_in_sender_reserve_on_ahr_after =
<AssetHubPezkuwichain as Chain>::account_data_of(sov_of_sender_on_ah).free;
let wnds_in_receiver_reserve_on_ahr_after = AssetHubPezkuwichain::execute_with(|| {
type Assets = <AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::ForeignAssets;
<Assets as Inspect<_>>::balance(
wnd_at_pezkuwichain_teyrchains.clone().try_into().unwrap(),
&sov_of_receiver_on_ah,
)
});
let rocs_in_receiver_reserve_on_ahr_after =
<AssetHubPezkuwichain as Chain>::account_data_of(sov_of_receiver_on_ah).free;
let receiver_rocs_after = PenpalB::execute_with(|| {
type ForeignAssets = <PenpalB as PenpalBPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(roc_location, &receiver)
});
let receiver_wnds_after = PenpalB::execute_with(|| {
type ForeignAssets = <PenpalB as PenpalBPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(wnd_at_pezkuwichain_teyrchains, &receiver)
});
// Sender's balance is reduced by amount sent plus delivery fees
assert!(sender_rocs_after < sender_rocs_before - roc_to_send);
assert_eq!(sender_wnds_after, sender_wnds_before - wnd_to_send);
// Sovereign accounts on reserve are changed accordingly
assert_eq!(
rocs_in_sender_reserve_on_ahr_after,
rocs_in_sender_reserve_on_ahr_before - roc_to_send
);
assert_eq!(
wnds_in_sender_reserve_on_ahr_after,
wnds_in_sender_reserve_on_ahr_before - wnd_to_send
);
assert!(rocs_in_receiver_reserve_on_ahr_after > rocs_in_receiver_reserve_on_ahr_before);
assert_eq!(
wnds_in_receiver_reserve_on_ahr_after,
wnds_in_receiver_reserve_on_ahr_before + wnd_to_send
);
// Receiver's balance is increased
assert!(receiver_rocs_after > receiver_rocs_before);
assert_eq!(receiver_wnds_after, receiver_wnds_before + wnd_to_send);
}
// ==============================================================================================
// ==== Bidirectional Transfer - Native + Teleportable Foreign Assets - Teyrchain<->AssetHub ====
// ==============================================================================================
/// Transfers of native asset plus teleportable foreign asset from Teyrchain to AssetHub and back
/// with fees paid using native asset.
#[test]
fn bidirectional_teleport_foreign_asset_between_para_and_asset_hub_using_explicit_transfer_types() {
do_bidirectional_teleport_foreign_assets_between_para_and_asset_hub_using_xt(
para_to_asset_hub_teleport_foreign_assets,
asset_hub_to_para_teleport_foreign_assets,
);
}
// ===============================================================
// ===== Transfer - Native Asset - Relay->AssetHub->Teyrchain ====
// ===============================================================
/// Transfers of native asset Relay to Teyrchain (using AssetHub reserve). Teyrchains want to avoid
/// managing SAs on all system chains, thus want all their HEZ-in-reserve to be held in their
/// Sovereign Account on Asset Hub.
#[test]
fn transfer_native_asset_from_relay_to_para_through_asset_hub() {
// Init values for Relay
let destination = Pezkuwichain::child_location_of(PenpalA::para_id());
let sender = PezkuwichainSender::get();
let amount_to_send: Balance = PEZKUWICHAIN_ED * 1000;
// Init values for Teyrchain
let relay_native_asset_location = RelayLocation::get();
let receiver = PenpalAReceiver::get();
// Init Test
let test_args = TestContext {
sender,
receiver: receiver.clone(),
args: TestArgs::new_relay(destination.clone(), receiver.clone(), amount_to_send),
};
let mut test = RelayToParaThroughAHTest::new(test_args);
let sov_penpal_on_ah = AssetHubPezkuwichain::sovereign_account_id_of(
AssetHubPezkuwichain::sibling_location_of(PenpalA::para_id()),
);
// Query initial balances
let sender_balance_before = test.sender.balance;
let sov_penpal_on_ah_before = AssetHubPezkuwichain::execute_with(|| {
<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::Balances::free_balance(
sov_penpal_on_ah.clone(),
)
});
let receiver_assets_before = PenpalA::execute_with(|| {
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(relay_native_asset_location.clone(), &receiver)
});
fn relay_assertions(t: RelayToParaThroughAHTest) {
type RuntimeEvent = <Pezkuwichain as Chain>::RuntimeEvent;
Pezkuwichain::assert_xcm_pallet_attempted_complete(None);
assert_expected_events!(
Pezkuwichain,
vec![
// Amount to teleport is withdrawn from Sender
RuntimeEvent::Balances(pezpallet_balances::Event::Burned { who, amount }) => {
who: *who == t.sender.account_id,
amount: *amount == t.args.amount,
},
// Amount to teleport is deposited in Relay's `CheckAccount`
RuntimeEvent::Balances(pezpallet_balances::Event::Minted { who, amount }) => {
who: *who == <Pezkuwichain as PezkuwichainPallet>::XcmPallet::check_account(),
amount: *amount == t.args.amount,
},
]
);
}
fn asset_hub_assertions(_: RelayToParaThroughAHTest) {
type RuntimeEvent = <AssetHubPezkuwichain as Chain>::RuntimeEvent;
let sov_penpal_on_ah = AssetHubPezkuwichain::sovereign_account_id_of(
AssetHubPezkuwichain::sibling_location_of(PenpalA::para_id()),
);
assert_expected_events!(
AssetHubPezkuwichain,
vec![
// Deposited to receiver teyrchain SA
RuntimeEvent::Balances(
pezpallet_balances::Event::Minted { who, .. }
) => {
who: *who == sov_penpal_on_ah,
},
RuntimeEvent::MessageQueue(
pezpallet_message_queue::Event::Processed { success: true, .. }
) => {},
]
);
}
fn penpal_assertions(t: RelayToParaThroughAHTest) {
type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent;
// Assets in t are relative to the relay chain. The asset here should be relative to
// Penpal, so parents: 1.
let expected_id: Location = Location { parents: 1, interior: Here };
assert_expected_events!(
PenpalA,
vec![
RuntimeEvent::ForeignAssets(pezpallet_assets::Event::Issued { asset_id, owner, .. }) => {
asset_id: *asset_id == expected_id,
owner: *owner == t.receiver.account_id,
},
]
);
}
fn transfer_assets_dispatchable(t: RelayToParaThroughAHTest) -> DispatchResult {
let fee: Asset = t
.args
.assets
.inner()
.iter()
.find(|a| a.id == t.args.fee_asset_id)
.cloned()
.unwrap();
let asset_hub_location = Pezkuwichain::child_location_of(AssetHubPezkuwichain::para_id());
let context = PezkuwichainUniversalLocation::get();
// reanchor fees to the view of destination (Penpal)
let mut remote_fees = fee.clone().reanchored(&t.args.dest, &context).unwrap();
if let Fungible(ref mut amount) = remote_fees.fun {
// we already spent some fees along the way, just use half of what we started with
*amount = *amount / 2;
}
let xcm_on_final_dest = Xcm::<()>(vec![
BuyExecution { fees: remote_fees, weight_limit: t.args.weight_limit.clone() },
DepositAsset {
assets: Wild(AllCounted(t.args.assets.len() as u32)),
beneficiary: t.args.beneficiary,
},
]);
// reanchor final dest (Penpal) to the view of hop (Asset Hub)
let mut dest = t.args.dest.clone();
dest.reanchor(&asset_hub_location, &context).unwrap();
// on Asset Hub, forward assets to Penpal
let xcm_on_hop = Xcm::<()>(vec![DepositReserveAsset {
assets: Wild(AllCounted(t.args.assets.len() as u32)),
dest,
xcm: xcm_on_final_dest,
}]);
Dmp::make_teyrchain_reachable(AssetHubPezkuwichain::para_id());
// First leg is a teleport, from there a local-reserve-transfer to final dest
<Pezkuwichain as PezkuwichainPallet>::XcmPallet::transfer_assets_using_type_and_then(
t.signed_origin,
bx!(asset_hub_location.into()),
bx!(t.args.assets.into()),
bx!(TransferType::Teleport),
bx!(fee.id.into()),
bx!(TransferType::Teleport),
bx!(VersionedXcm::from(xcm_on_hop)),
t.args.weight_limit,
)
}
// Set assertions and dispatchables
test.set_assertion::<Pezkuwichain>(relay_assertions);
test.set_assertion::<AssetHubPezkuwichain>(asset_hub_assertions);
test.set_assertion::<PenpalA>(penpal_assertions);
test.set_dispatchable::<Pezkuwichain>(transfer_assets_dispatchable);
test.assert();
// Query final balances
let sender_balance_after = test.sender.balance;
let sov_penpal_on_ah_after = AssetHubPezkuwichain::execute_with(|| {
<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::Balances::free_balance(
sov_penpal_on_ah,
)
});
let receiver_assets_after = PenpalA::execute_with(|| {
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(relay_native_asset_location, &receiver)
});
// Sender's balance is reduced by amount sent plus delivery fees
assert!(sender_balance_after < sender_balance_before - amount_to_send);
// SA on AH balance is increased
assert!(sov_penpal_on_ah_after > sov_penpal_on_ah_before);
// Receiver's asset balance is increased
assert!(receiver_assets_after > receiver_assets_before);
// Receiver's asset balance increased by `amount_to_send - delivery_fees - bought_execution`;
// `delivery_fees` might be paid from transfer or JIT, also `bought_execution` is unknown but
// should be non-zero
assert!(receiver_assets_after < receiver_assets_before + amount_to_send);
}
@@ -0,0 +1,102 @@
// 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.
mod claim_assets;
mod hybrid_transfers;
mod reserve_transfer;
mod reward_pool;
mod send;
mod set_xcm_versions;
mod swap;
mod teleport;
mod treasury;
mod xcm_fee_estimation;
#[macro_export]
macro_rules! create_pool_with_roc_on {
// default amounts
( $chain:ident, $asset_id:expr, $is_foreign:expr, $asset_owner:expr ) => {
$crate::create_pool_with_roc_on!(
$chain,
$asset_id,
$is_foreign,
$asset_owner,
1_000_000_000_000,
2_000_000_000_000
);
};
// custom amounts
( $chain:ident, $asset_id:expr, $is_foreign:expr, $asset_owner:expr, $roc_amount:expr, $asset_amount:expr ) => {
emulated_integration_tests_common::impls::paste::paste! {
<$chain>::execute_with(|| {
type RuntimeEvent = <$chain as Chain>::RuntimeEvent;
let owner = $asset_owner;
let signed_owner = <$chain as Chain>::RuntimeOrigin::signed(owner.clone());
let roc_location: Location = Parent.into();
if $is_foreign {
assert_ok!(<$chain as [<$chain Pallet>]>::ForeignAssets::mint(
signed_owner.clone(),
$asset_id.clone().into(),
owner.clone().into(),
10_000_000_000_000, // For it to have more than enough.
));
} else {
let asset_id = match $asset_id.interior.last() {
Some(GeneralIndex(id)) => *id as u32,
_ => unreachable!(),
};
assert_ok!(<$chain as [<$chain Pallet>]>::Assets::mint(
signed_owner.clone(),
asset_id.into(),
owner.clone().into(),
10_000_000_000_000, // For it to have more than enough.
));
}
assert_ok!(<$chain as [<$chain Pallet>]>::AssetConversion::create_pool(
signed_owner.clone(),
Box::new(roc_location.clone()),
Box::new($asset_id.clone()),
));
assert_expected_events!(
$chain,
vec![
RuntimeEvent::AssetConversion(pezpallet_asset_conversion::Event::PoolCreated { .. }) => {},
]
);
assert_ok!(<$chain as [<$chain Pallet>]>::AssetConversion::add_liquidity(
signed_owner,
Box::new(roc_location),
Box::new($asset_id),
$roc_amount,
$asset_amount,
0,
0,
owner.into()
));
assert_expected_events!(
$chain,
vec![
RuntimeEvent::AssetConversion(pezpallet_asset_conversion::Event::LiquidityAdded { .. }) => {},
]
);
});
}
};
}
@@ -0,0 +1,114 @@
// 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 pezframe_support::{pezsp_runtime::traits::Dispatchable, traits::schedule::DispatchTime};
use xcm_executor::traits::ConvertLocation;
#[test]
fn treasury_creates_asset_reward_pool() {
AssetHubPezkuwichain::execute_with(|| {
type RuntimeEvent = <AssetHubPezkuwichain as Chain>::RuntimeEvent;
type Balances = <AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::Balances;
let treasurer =
Location::new(1, [Plurality { id: BodyId::Treasury, part: BodyPart::Voice }]);
let treasurer_account =
ahr_xcm_config::LocationToAccountId::convert_location(&treasurer).unwrap();
assert_ok!(Balances::force_set_balance(
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::root(),
treasurer_account.clone().into(),
ASSET_HUB_PEZKUWICHAIN_ED * 100_000,
));
let events = AssetHubPezkuwichain::events();
match events.iter().last() {
Some(RuntimeEvent::Balances(pezpallet_balances::Event::BalanceSet { who, .. })) => {
assert_eq!(*who, treasurer_account)
},
_ => panic!("Expected Balances::BalanceSet event"),
}
});
Pezkuwichain::execute_with(|| {
type AssetHubPezkuwichainRuntimeCall = <AssetHubPezkuwichain as Chain>::RuntimeCall;
type AssetHubPezkuwichainRuntime = <AssetHubPezkuwichain as Chain>::Runtime;
type PezkuwichainRuntimeCall = <Pezkuwichain as Chain>::RuntimeCall;
type PezkuwichainRuntime = <Pezkuwichain as Chain>::Runtime;
type PezkuwichainRuntimeEvent = <Pezkuwichain as Chain>::RuntimeEvent;
type PezkuwichainRuntimeOrigin = <Pezkuwichain as Chain>::RuntimeOrigin;
Dmp::make_teyrchain_reachable(AssetHubPezkuwichain::para_id());
let staked_asset_id = bx!(RelayLocation::get());
let reward_asset_id = bx!(RelayLocation::get());
let reward_rate_per_block = 1_000_000_000;
let lifetime = 1_000_000_000;
let admin = None;
let create_pool_call =
PezkuwichainRuntimeCall::XcmPallet(pezpallet_xcm::Call::<PezkuwichainRuntime>::send {
dest: bx!(VersionedLocation::V4(
xcm::v4::Junction::Teyrchain(AssetHubPezkuwichain::para_id().into()).into()
)),
message: bx!(VersionedXcm::V5(Xcm(vec![
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
Transact {
origin_kind: OriginKind::SovereignAccount,
fallback_max_weight: None,
call: AssetHubPezkuwichainRuntimeCall::AssetRewards(
pezpallet_asset_rewards::Call::<AssetHubPezkuwichainRuntime>::create_pool {
staked_asset_id,
reward_asset_id,
reward_rate_per_block,
expiry: DispatchTime::After(lifetime),
admin
}
)
.encode()
.into(),
}
]))),
});
let treasury_origin: PezkuwichainRuntimeOrigin = Treasurer.into();
assert_ok!(create_pool_call.dispatch(treasury_origin));
assert_expected_events!(
Pezkuwichain,
vec![
PezkuwichainRuntimeEvent::XcmPallet(pezpallet_xcm::Event::Sent { .. }) => {},
]
);
});
AssetHubPezkuwichain::execute_with(|| {
type Runtime = <AssetHubPezkuwichain as Chain>::Runtime;
type RuntimeEvent = <AssetHubPezkuwichain as Chain>::RuntimeEvent;
assert_eq!(1, pezpallet_asset_rewards::Pools::<Runtime>::iter().count());
let events = AssetHubPezkuwichain::events();
match events.iter().last() {
Some(RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed {
success: true,
..
})) => (),
_ => panic!("Expected MessageQueue::Processed event"),
}
});
}
@@ -0,0 +1,197 @@
// 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::{create_pool_with_roc_on, imports::*};
/// Relay Chain should be able to execute `Transact` instructions in System Teyrchain
/// when `OriginKind::Superuser`.
#[test]
fn send_transact_as_superuser_from_relay_to_asset_hub_works() {
AssetHubPezkuwichain::force_create_asset_from_relay_as_root(
ASSET_ID,
ASSET_MIN_BALANCE,
true,
AssetHubPezkuwichainSender::get().into(),
Some(Weight::from_parts(144_933_000, 3675)),
)
}
/// We tests two things here:
/// - Teyrchain should be able to send XCM paying its fee at Asset Hub using system asset
/// - Teyrchain should be able to create a new Foreign Asset at Asset Hub
#[test]
fn send_xcm_from_para_to_asset_hub_paying_fee_with_system_asset() {
let para_sovereign_account = AssetHubPezkuwichain::sovereign_account_id_of(
AssetHubPezkuwichain::sibling_location_of(PenpalA::para_id()),
);
let asset_location_on_penpal = Location::new(
0,
[Junction::PalletInstance(ASSETS_PALLET_ID), Junction::GeneralIndex(ASSET_ID.into())],
);
let foreign_asset_at_asset_hub =
Location::new(1, [Junction::Teyrchain(PenpalA::para_id().into())])
.appended_with(asset_location_on_penpal)
.unwrap();
// Encoded `create_asset` call to be executed in AssetHub
let call = AssetHubPezkuwichain::create_foreign_asset_call(
foreign_asset_at_asset_hub.clone(),
ASSET_MIN_BALANCE,
para_sovereign_account.clone(),
);
let origin_kind = OriginKind::Xcm;
let fee_amount = ASSET_HUB_PEZKUWICHAIN_ED * 1000000;
let system_asset = (Parent, fee_amount).into();
let root_origin = <PenpalA as Chain>::RuntimeOrigin::root();
let system_para_destination =
PenpalA::sibling_location_of(AssetHubPezkuwichain::para_id()).into();
let xcm = xcm_transact_paid_execution(
call,
origin_kind,
system_asset,
para_sovereign_account.clone(),
);
// SA-of-Penpal-on-AHR needs to have balance to pay for fees and asset creation deposit
AssetHubPezkuwichain::fund_accounts(vec![(
para_sovereign_account.clone().into(),
ASSET_HUB_PEZKUWICHAIN_ED * 10000000000,
)]);
PenpalA::execute_with(|| {
assert_ok!(<PenpalA as PenpalAPallet>::PezkuwiXcm::send(
root_origin,
bx!(system_para_destination),
bx!(xcm),
));
PenpalA::assert_xcm_pallet_sent();
});
AssetHubPezkuwichain::execute_with(|| {
type RuntimeEvent = <AssetHubPezkuwichain as Chain>::RuntimeEvent;
AssetHubPezkuwichain::assert_xcmp_queue_success(None);
assert_expected_events!(
AssetHubPezkuwichain,
vec![
// Burned the fee
RuntimeEvent::Balances(pezpallet_balances::Event::Burned { who, amount }) => {
who: *who == para_sovereign_account,
amount: *amount == fee_amount,
},
// Foreign Asset created
RuntimeEvent::ForeignAssets(pezpallet_assets::Event::Created { asset_id, creator, owner }) => {
asset_id: *asset_id == foreign_asset_at_asset_hub,
creator: *creator == para_sovereign_account.clone(),
owner: *owner == para_sovereign_account,
},
]
);
type ForeignAssets = <AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::ForeignAssets;
assert!(ForeignAssets::asset_exists(foreign_asset_at_asset_hub));
});
}
/// We tests two things here:
/// - Teyrchain should be able to send XCM paying its fee at Asset Hub using sufficient asset
/// - Teyrchain should be able to create a new Asset at Asset Hub
#[test]
fn send_xcm_from_para_to_asset_hub_paying_fee_with_sufficient_asset() {
let para_sovereign_account = AssetHubPezkuwichain::sovereign_account_id_of(
AssetHubPezkuwichain::sibling_location_of(PenpalA::para_id()),
);
// Force create and mint sufficient assets for Teyrchain's sovereign account
AssetHubPezkuwichain::force_create_and_mint_asset(
ASSET_ID,
ASSET_MIN_BALANCE,
true,
para_sovereign_account.clone(),
Some(Weight::from_parts(144_933_000, 3675)),
ASSET_MIN_BALANCE * 1000000000,
);
// Just a different `asset_id`` that does not exist yet
let new_asset_id = ASSET_ID + 1;
// Encoded `create_asset` call to be executed in AssetHub
let call = AssetHubPezkuwichain::create_asset_call(
new_asset_id,
ASSET_MIN_BALANCE,
para_sovereign_account.clone(),
);
let origin_kind = OriginKind::SovereignAccount;
let fee_amount = ASSET_MIN_BALANCE * 1000000;
let asset =
([PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())], fee_amount).into();
let asset_location =
Location::new(0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())]);
let root_origin = <PenpalA as Chain>::RuntimeOrigin::root();
let system_para_destination =
PenpalA::sibling_location_of(AssetHubPezkuwichain::para_id()).into();
let xcm = xcm_transact_paid_execution(call, origin_kind, asset, para_sovereign_account.clone());
// SA-of-Penpal-on-AHR needs to have balance to pay for asset creation deposit
AssetHubPezkuwichain::fund_accounts(vec![(
para_sovereign_account.clone().into(),
ASSET_HUB_PEZKUWICHAIN_ED * 10000000000,
)]);
create_pool_with_roc_on!(
AssetHubPezkuwichain,
asset_location,
false,
para_sovereign_account.clone(),
9_000_000_000_000_000,
9_000_000_000_000
);
PenpalA::execute_with(|| {
assert_ok!(<PenpalA as PenpalAPallet>::PezkuwiXcm::send(
root_origin,
bx!(system_para_destination),
bx!(xcm),
));
PenpalA::assert_xcm_pallet_sent();
});
AssetHubPezkuwichain::execute_with(|| {
type RuntimeEvent = <AssetHubPezkuwichain as Chain>::RuntimeEvent;
AssetHubPezkuwichain::assert_xcmp_queue_success(None);
assert_expected_events!(
AssetHubPezkuwichain,
vec![
// Burned the fee
RuntimeEvent::Assets(pezpallet_assets::Event::Burned { asset_id, owner, balance }) => {
asset_id: *asset_id == ASSET_ID,
owner: *owner == para_sovereign_account,
balance: *balance == fee_amount,
},
// Asset created
RuntimeEvent::Assets(pezpallet_assets::Event::Created { asset_id, creator, owner }) => {
asset_id: *asset_id == new_asset_id,
creator: *creator == para_sovereign_account.clone(),
owner: *owner == para_sovereign_account,
},
]
);
});
}
@@ -0,0 +1,83 @@
// 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::*;
#[test]
fn relay_sets_system_para_xcm_supported_version() {
// Init tests variables
let sudo_origin = <Pezkuwichain as Chain>::RuntimeOrigin::root();
let system_para_destination: Location =
Pezkuwichain::child_location_of(AssetHubPezkuwichain::para_id());
// Relay Chain sets supported version for Asset Teyrchain
Pezkuwichain::execute_with(|| {
assert_ok!(<Pezkuwichain as PezkuwichainPallet>::XcmPallet::force_xcm_version(
sudo_origin,
bx!(system_para_destination.clone()),
XCM_V3
));
type RuntimeEvent = <Pezkuwichain as Chain>::RuntimeEvent;
assert_expected_events!(
Pezkuwichain,
vec![
RuntimeEvent::XcmPallet(pezpallet_xcm::Event::SupportedVersionChanged {
location,
version: XCM_V3
}) => { location: *location == system_para_destination, },
]
);
});
}
#[test]
fn system_para_sets_relay_xcm_supported_version() {
// Init test variables
let parent_location = AssetHubPezkuwichain::parent_location();
let force_xcm_version_call =
<AssetHubPezkuwichain as Chain>::RuntimeCall::PezkuwiXcm(pezpallet_xcm::Call::<
<AssetHubPezkuwichain as Chain>::Runtime,
>::force_xcm_version {
location: bx!(parent_location.clone()),
version: XCM_V3,
})
.encode()
.into();
// System Teyrchain sets supported version for Relay Chain through it
Pezkuwichain::send_unpaid_transact_to_teyrchain_as_root(
AssetHubPezkuwichain::para_id(),
force_xcm_version_call,
);
// System Teyrchain receive the XCM message
AssetHubPezkuwichain::execute_with(|| {
type RuntimeEvent = <AssetHubPezkuwichain as Chain>::RuntimeEvent;
AssetHubPezkuwichain::assert_dmp_queue_complete(Some(Weight::from_parts(115_294_000, 0)));
assert_expected_events!(
AssetHubPezkuwichain,
vec![
RuntimeEvent::PezkuwiXcm(pezpallet_xcm::Event::SupportedVersionChanged {
location,
version: XCM_V3
}) => { location: *location == parent_location, },
]
);
});
}
@@ -0,0 +1,449 @@
// 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::*;
#[test]
fn swap_locally_on_chain_using_local_assets() {
let asset_native = Box::new(Location::try_from(RelayLocation::get()).unwrap());
let asset_one = Box::new(Location::new(
0,
[Junction::PalletInstance(ASSETS_PALLET_ID), Junction::GeneralIndex(ASSET_ID.into())],
));
AssetHubPezkuwichain::execute_with(|| {
type RuntimeEvent = <AssetHubPezkuwichain as Chain>::RuntimeEvent;
assert_ok!(<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::Assets::create(
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(
AssetHubPezkuwichainSender::get()
),
ASSET_ID.into(),
AssetHubPezkuwichainSender::get().into(),
1000,
));
assert!(<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::Assets::asset_exists(
ASSET_ID
));
assert_ok!(<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::Assets::mint(
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(
AssetHubPezkuwichainSender::get()
),
ASSET_ID.into(),
AssetHubPezkuwichainSender::get().into(),
100_000_000_000_000,
));
assert_ok!(
<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::AssetConversion::create_pool(
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(
AssetHubPezkuwichainSender::get()
),
asset_native.clone(),
asset_one.clone(),
)
);
assert_expected_events!(
AssetHubPezkuwichain,
vec![
RuntimeEvent::AssetConversion(pezpallet_asset_conversion::Event::PoolCreated { .. }) => {},
]
);
assert_ok!(
<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::AssetConversion::add_liquidity(
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(
AssetHubPezkuwichainSender::get()
),
asset_native.clone(),
asset_one.clone(),
1_000_000_000_000,
2_000_000_000_000,
0,
0,
AssetHubPezkuwichainSender::get().into()
)
);
assert_expected_events!(
AssetHubPezkuwichain,
vec![
RuntimeEvent::AssetConversion(pezpallet_asset_conversion::Event::LiquidityAdded {lp_token_minted, .. }) => { lp_token_minted: *lp_token_minted == 1414213562273, },
]
);
let path = vec![asset_native.clone(), asset_one.clone()];
assert_ok!(
<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::AssetConversion::swap_exact_tokens_for_tokens(
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(AssetHubPezkuwichainSender::get()),
path,
100,
1,
AssetHubPezkuwichainSender::get().into(),
true
)
);
assert_expected_events!(
AssetHubPezkuwichain,
vec![
RuntimeEvent::AssetConversion(pezpallet_asset_conversion::Event::SwapExecuted { amount_in, amount_out, .. }) => {
amount_in: *amount_in == 100,
amount_out: *amount_out == 199,
},
]
);
assert_ok!(
<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::AssetConversion::remove_liquidity(
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(
AssetHubPezkuwichainSender::get()
),
asset_native,
asset_one,
1414213562273 - ASSET_HUB_PEZKUWICHAIN_ED * 2, /* all but the 2 EDs can't be
* retrieved. */
0,
0,
AssetHubPezkuwichainSender::get().into(),
)
);
});
}
#[test]
fn swap_locally_on_chain_using_foreign_assets() {
let asset_native = Box::new(Location::try_from(RelayLocation::get()).unwrap());
let asset_location_on_penpal = PenpalA::execute_with(|| {
Location::try_from(PenpalLocalTeleportableToAssetHub::get()).unwrap()
});
let foreign_asset_at_asset_hub_pezkuwichain =
Location::new(1, [Junction::Teyrchain(PenpalA::para_id().into())])
.appended_with(asset_location_on_penpal)
.unwrap();
let penpal_as_seen_by_ah = AssetHubPezkuwichain::sibling_location_of(PenpalA::para_id());
let sov_penpal_on_ahr = AssetHubPezkuwichain::sovereign_account_id_of(penpal_as_seen_by_ah);
AssetHubPezkuwichain::fund_accounts(vec![
// An account to swap dot for something else.
(AssetHubPezkuwichainSender::get().into(), 5_000_000 * ASSET_HUB_PEZKUWICHAIN_ED),
// Penpal's sovereign account in AH should have some balance
(sov_penpal_on_ahr.clone().into(), 100_000_000 * ASSET_HUB_PEZKUWICHAIN_ED),
]);
AssetHubPezkuwichain::execute_with(|| {
// 0: No need to create foreign asset as it exists in genesis.
//
// 1: Mint foreign asset on asset_hub_pezkuwichain:
//
// (While it might be nice to use batch,
// currently that's disabled due to safe call filters.)
type RuntimeEvent = <AssetHubPezkuwichain as Chain>::RuntimeEvent;
// 1. Mint foreign asset (in reality this should be a teleport or some such)
assert_ok!(<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::ForeignAssets::mint(
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(
sov_penpal_on_ahr.clone().into()
),
foreign_asset_at_asset_hub_pezkuwichain.clone(),
sov_penpal_on_ahr.clone().into(),
ASSET_HUB_PEZKUWICHAIN_ED * 3_000_000_000_000,
));
assert_expected_events!(
AssetHubPezkuwichain,
vec![
RuntimeEvent::ForeignAssets(pezpallet_assets::Event::Issued { .. }) => {},
]
);
// 2. Create pool:
assert_ok!(
<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::AssetConversion::create_pool(
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(
AssetHubPezkuwichainSender::get()
),
asset_native.clone(),
Box::new(foreign_asset_at_asset_hub_pezkuwichain.clone()),
)
);
assert_expected_events!(
AssetHubPezkuwichain,
vec![
RuntimeEvent::AssetConversion(pezpallet_asset_conversion::Event::PoolCreated { .. }) => {},
]
);
// 3. Add liquidity:
assert_ok!(
<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::AssetConversion::add_liquidity(
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(sov_penpal_on_ahr.clone()),
asset_native.clone(),
Box::new(foreign_asset_at_asset_hub_pezkuwichain.clone()),
1_000_000_000_000,
2_000_000_000_000,
0,
0,
sov_penpal_on_ahr.clone().into()
)
);
assert_expected_events!(
AssetHubPezkuwichain,
vec![
RuntimeEvent::AssetConversion(pezpallet_asset_conversion::Event::LiquidityAdded {lp_token_minted, .. }) => {
lp_token_minted: *lp_token_minted == 1414213562273,
},
]
);
// 4. Swap!
let path =
vec![asset_native.clone(), Box::new(foreign_asset_at_asset_hub_pezkuwichain.clone())];
assert_ok!(
<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::AssetConversion::swap_exact_tokens_for_tokens(
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(AssetHubPezkuwichainSender::get()),
path,
100000 * ASSET_HUB_PEZKUWICHAIN_ED,
1000 * ASSET_HUB_PEZKUWICHAIN_ED,
AssetHubPezkuwichainSender::get().into(),
true
)
);
assert_expected_events!(
AssetHubPezkuwichain,
vec![
RuntimeEvent::AssetConversion(pezpallet_asset_conversion::Event::SwapExecuted { amount_in, amount_out, .. },) => {
amount_in: *amount_in == 333333300000,
amount_out: *amount_out == 498874118173,
},
]
);
// 5. Remove liquidity
assert_ok!(
<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::AssetConversion::remove_liquidity(
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(sov_penpal_on_ahr.clone()),
asset_native.clone(),
Box::new(foreign_asset_at_asset_hub_pezkuwichain.clone()),
1414213562273 - ASSET_HUB_PEZKUWICHAIN_ED * 2, /* all but the 2 EDs can't be
* retrieved. */
0,
0,
sov_penpal_on_ahr.clone().into(),
)
);
});
}
#[test]
fn cannot_create_pool_from_pool_assets() {
let asset_native = RelayLocation::get();
let mut asset_one = ahr_xcm_config::PoolAssetsPalletLocation::get();
asset_one.append_with(GeneralIndex(ASSET_ID.into())).expect("pool assets");
AssetHubPezkuwichain::execute_with(|| {
let pool_owner_account_id = AssetHubPezkuwichainAssetConversionOrigin::get();
assert_ok!(<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::PoolAssets::create(
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(pool_owner_account_id.clone()),
ASSET_ID.into(),
pool_owner_account_id.clone().into(),
1000,
));
assert!(<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::PoolAssets::asset_exists(
ASSET_ID
));
assert_ok!(<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::PoolAssets::mint(
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(pool_owner_account_id),
ASSET_ID.into(),
AssetHubPezkuwichainSender::get().into(),
3_000_000_000_000,
));
assert_matches::assert_matches!(
<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::AssetConversion::create_pool(
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(AssetHubPezkuwichainSender::get()),
Box::new(Location::try_from(asset_native).unwrap()),
Box::new(Location::try_from(asset_one).unwrap()),
),
Err(DispatchError::Module(ModuleError{index: _, error: _, message})) => assert_eq!(message, Some("Unknown"))
);
});
}
#[test]
fn pay_xcm_fee_with_some_asset_swapped_for_native() {
let asset_native = Location::try_from(RelayLocation::get()).unwrap();
let asset_one = Location {
parents: 0,
interior: [
Junction::PalletInstance(ASSETS_PALLET_ID),
Junction::GeneralIndex(ASSET_ID.into()),
]
.into(),
};
let penpal = AssetHubPezkuwichain::sovereign_account_id_of(
AssetHubPezkuwichain::sibling_location_of(PenpalA::para_id()),
);
AssetHubPezkuwichain::execute_with(|| {
type RuntimeEvent = <AssetHubPezkuwichain as Chain>::RuntimeEvent;
// set up pool with ASSET_ID <> NATIVE pair
assert_ok!(<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::Assets::create(
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(
AssetHubPezkuwichainSender::get()
),
ASSET_ID.into(),
AssetHubPezkuwichainSender::get().into(),
ASSET_MIN_BALANCE,
));
assert!(<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::Assets::asset_exists(
ASSET_ID
));
assert_ok!(<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::Assets::mint(
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(
AssetHubPezkuwichainSender::get()
),
ASSET_ID.into(),
AssetHubPezkuwichainSender::get().into(),
3_000_000_000_000,
));
assert_ok!(
<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::AssetConversion::create_pool(
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(
AssetHubPezkuwichainSender::get()
),
Box::new(asset_native.clone()),
Box::new(asset_one.clone()),
)
);
assert_expected_events!(
AssetHubPezkuwichain,
vec![
RuntimeEvent::AssetConversion(pezpallet_asset_conversion::Event::PoolCreated { .. }) => {},
]
);
assert_ok!(
<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::AssetConversion::add_liquidity(
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(
AssetHubPezkuwichainSender::get()
),
Box::new(asset_native),
Box::new(asset_one),
1_000_000_000_000,
2_000_000_000_000,
0,
0,
AssetHubPezkuwichainSender::get().into()
)
);
assert_expected_events!(
AssetHubPezkuwichain,
vec![
RuntimeEvent::AssetConversion(pezpallet_asset_conversion::Event::LiquidityAdded {lp_token_minted, .. }) => { lp_token_minted: *lp_token_minted == 1414213562273, },
]
);
// ensure `penpal` sovereign account has no native tokens and mint some `ASSET_ID`
assert_eq!(
<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::Balances::free_balance(
penpal.clone()
),
0
);
assert_ok!(<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::Assets::touch_other(
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(
AssetHubPezkuwichainSender::get()
),
ASSET_ID.into(),
penpal.clone().into(),
));
assert_ok!(<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::Assets::mint(
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(
AssetHubPezkuwichainSender::get()
),
ASSET_ID.into(),
penpal.clone().into(),
10_000_000_000_000,
));
});
PenpalA::execute_with(|| {
// send xcm transact from `penpal` account while paying with `ASSET_ID` tokens on
// `AssetHubPezkuwichain`
let call = <AssetHubPezkuwichain as Chain>::RuntimeCall::System(pezframe_system::Call::<
<AssetHubPezkuwichain as Chain>::Runtime,
>::remark {
remark: vec![],
})
.encode()
.into();
let penpal_root = <PenpalA as Chain>::RuntimeOrigin::root();
let fee_amount = 4_000_000_000_000u128;
let asset_one =
([PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())], fee_amount).into();
let asset_hub_location =
PenpalA::sibling_location_of(AssetHubPezkuwichain::para_id()).into();
let xcm = xcm_transact_paid_execution(
call,
OriginKind::SovereignAccount,
asset_one,
penpal.clone(),
);
assert_ok!(<PenpalA as PenpalAPallet>::PezkuwiXcm::send(
penpal_root,
bx!(asset_hub_location),
bx!(xcm),
));
PenpalA::assert_xcm_pallet_sent();
});
AssetHubPezkuwichain::execute_with(|| {
type RuntimeEvent = <AssetHubPezkuwichain as Chain>::RuntimeEvent;
AssetHubPezkuwichain::assert_xcmp_queue_success(None);
assert_expected_events!(
AssetHubPezkuwichain,
vec![
RuntimeEvent::AssetConversion(pezpallet_asset_conversion::Event::SwapCreditExecuted { .. },) => {},
RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed { success: true,.. }) => {},
]
);
});
}
#[test]
fn xcm_fee_querying_apis_work() {
test_xcm_fee_querying_apis_work_for_asset_hub!(AssetHubPezkuwichain);
}
@@ -0,0 +1,643 @@
// 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::*;
fn relay_dest_assertions_fail(_t: SystemParaToRelayTest) {
Pezkuwichain::assert_ump_queue_processed(
false,
Some(AssetHubPezkuwichain::para_id()),
Some(Weight::from_parts(157_718_000, 3_593)),
);
}
fn para_origin_assertions(t: SystemParaToRelayTest) {
type RuntimeEvent = <AssetHubPezkuwichain as Chain>::RuntimeEvent;
AssetHubPezkuwichain::assert_xcm_pallet_attempted_complete(Some(Weight::from_parts(
730_053_000,
4_000,
)));
AssetHubPezkuwichain::assert_teyrchain_system_ump_sent();
assert_expected_events!(
AssetHubPezkuwichain,
vec![
// Amount is withdrawn from Sender's account
RuntimeEvent::Balances(pezpallet_balances::Event::Burned { who, amount }) => {
who: *who == t.sender.account_id,
amount: *amount == t.args.amount,
},
]
);
}
fn penpal_to_ah_foreign_assets_sender_assertions(t: ParaToSystemParaTest) {
type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent;
let system_para_native_asset_location = RelayLocation::get();
let expected_asset_id = t.args.asset_id.unwrap();
let (_, expected_asset_amount) = non_fee_asset(&t.args.assets, &t.args.fee_asset_id).unwrap();
PenpalA::assert_xcm_pallet_attempted_complete(None);
assert_expected_events!(
PenpalA,
vec![
RuntimeEvent::ForeignAssets(
pezpallet_assets::Event::Burned { asset_id, owner, .. }
) => {
asset_id: *asset_id == system_para_native_asset_location,
owner: *owner == t.sender.account_id,
},
RuntimeEvent::Assets(pezpallet_assets::Event::Burned { asset_id, owner, balance }) => {
asset_id: *asset_id == expected_asset_id,
owner: *owner == t.sender.account_id,
balance: *balance == expected_asset_amount,
},
]
);
}
fn penpal_to_ah_foreign_assets_receiver_assertions(t: ParaToSystemParaTest) {
type RuntimeEvent = <AssetHubPezkuwichain as Chain>::RuntimeEvent;
let sov_penpal_on_ahr = AssetHubPezkuwichain::sovereign_account_id_of(
AssetHubPezkuwichain::sibling_location_of(PenpalA::para_id()),
);
let (_, expected_foreign_asset_amount) =
non_fee_asset(&t.args.assets, &t.args.fee_asset_id).unwrap();
let (_, fee_asset_amount) = fee_asset(&t.args.assets, &t.args.fee_asset_id).unwrap();
AssetHubPezkuwichain::assert_xcmp_queue_success(None);
assert_expected_events!(
AssetHubPezkuwichain,
vec![
// native asset reserve transfer for paying fees, withdrawn from Penpal's sov account
RuntimeEvent::Balances(
pezpallet_balances::Event::Burned { who, amount }
) => {
who: *who == sov_penpal_on_ahr.clone().into(),
amount: *amount == fee_asset_amount,
},
RuntimeEvent::Balances(pezpallet_balances::Event::Minted { who, .. }) => {
who: *who == t.receiver.account_id,
},
RuntimeEvent::ForeignAssets(pezpallet_assets::Event::Issued { asset_id, owner, amount }) => {
asset_id: *asset_id == PenpalATeleportableAssetLocation::get(),
owner: *owner == t.receiver.account_id,
amount: *amount == expected_foreign_asset_amount,
},
RuntimeEvent::Balances(pezpallet_balances::Event::Deposit { .. }) => {},
]
);
}
fn ah_to_penpal_foreign_assets_sender_assertions(t: SystemParaToParaTest) {
type RuntimeEvent = <AssetHubPezkuwichain as Chain>::RuntimeEvent;
AssetHubPezkuwichain::assert_xcm_pallet_attempted_complete(None);
let (expected_foreign_asset_id, expected_foreign_asset_amount) =
non_fee_asset(&t.args.assets, &t.args.fee_asset_id).unwrap();
let (_, fee_asset_amount) = fee_asset(&t.args.assets, &t.args.fee_asset_id).unwrap();
assert_expected_events!(
AssetHubPezkuwichain,
vec![
// native asset used for fees is transferred to Teyrchain's Sovereign account as reserve
RuntimeEvent::Balances(
pezpallet_balances::Event::Transfer { from, to, amount }
) => {
from: *from == t.sender.account_id,
to: *to == AssetHubPezkuwichain::sovereign_account_id_of(
t.args.dest.clone()
),
amount: *amount == fee_asset_amount,
},
// foreign asset is burned locally as part of teleportation
RuntimeEvent::ForeignAssets(pezpallet_assets::Event::Burned { asset_id, owner, balance }) => {
asset_id: *asset_id == expected_foreign_asset_id,
owner: *owner == t.sender.account_id,
balance: *balance == expected_foreign_asset_amount,
},
]
);
}
fn ah_to_penpal_foreign_assets_receiver_assertions(t: SystemParaToParaTest) {
type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent;
let expected_asset_id = t.args.asset_id.unwrap();
let (_, expected_asset_amount) = non_fee_asset(&t.args.assets, &t.args.fee_asset_id).unwrap();
let checking_account = <PenpalA as PenpalAPallet>::PezkuwiXcm::check_account();
let system_para_native_asset_location = RelayLocation::get();
PenpalA::assert_xcmp_queue_success(None);
assert_expected_events!(
PenpalA,
vec![
// checking account burns local asset as part of incoming teleport
RuntimeEvent::Assets(pezpallet_assets::Event::Burned { asset_id, owner, balance }) => {
asset_id: *asset_id == expected_asset_id,
owner: *owner == checking_account,
balance: *balance == expected_asset_amount,
},
// local asset is teleported into account of receiver
RuntimeEvent::Assets(pezpallet_assets::Event::Issued { asset_id, owner, amount }) => {
asset_id: *asset_id == expected_asset_id,
owner: *owner == t.receiver.account_id,
amount: *amount == expected_asset_amount,
},
// native asset for fee is deposited to receiver
RuntimeEvent::ForeignAssets(pezpallet_assets::Event::Issued { asset_id, owner, .. }) => {
asset_id: *asset_id == system_para_native_asset_location,
owner: *owner == t.receiver.account_id,
},
]
);
}
fn system_para_limited_teleport_assets(t: SystemParaToRelayTest) -> DispatchResult {
<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::PezkuwiXcm::limited_teleport_assets(
t.signed_origin,
bx!(t.args.dest.into()),
bx!(t.args.beneficiary.into()),
bx!(t.args.assets.into()),
bx!(t.args.fee_asset_id.into()),
t.args.weight_limit,
)
}
fn para_to_system_para_transfer_assets(t: ParaToSystemParaTest) -> DispatchResult {
<PenpalA as PenpalAPallet>::PezkuwiXcm::transfer_assets_using_type_and_then(
t.signed_origin,
bx!(t.args.dest.into()),
bx!(t.args.assets.into()),
bx!(TransferType::Teleport),
bx!(t.args.fee_asset_id.into()),
bx!(TransferType::DestinationReserve),
bx!(VersionedXcm::from(
Xcm::<()>::builder_unsafe()
.deposit_asset(AllCounted(2), t.args.beneficiary)
.build()
)),
t.args.weight_limit,
)
}
fn system_para_to_para_transfer_assets(t: SystemParaToParaTest) -> DispatchResult {
<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::PezkuwiXcm::transfer_assets_using_type_and_then(
t.signed_origin,
bx!(t.args.dest.into()),
bx!(t.args.assets.into()),
bx!(TransferType::Teleport),
bx!(t.args.fee_asset_id.into()),
bx!(TransferType::LocalReserve),
bx!(VersionedXcm::from(
Xcm::<()>::builder_unsafe()
.deposit_asset(AllCounted(2), t.args.beneficiary)
.build()
)),
t.args.weight_limit,
)
}
#[test]
fn teleport_via_limited_teleport_assets_to_other_system_teyrchains_works() {
let amount = ASSET_HUB_PEZKUWICHAIN_ED * 100;
let native_asset: Assets = (Parent, amount).into();
let fee_asset_id: AssetId = Parent.into();
test_teyrchain_is_trusted_teleporter!(
AssetHubPezkuwichain, // Origin
vec![BridgeHubPezkuwichain], // Destinations
(native_asset, amount),
fee_asset_id,
limited_teleport_assets
);
}
#[test]
fn teleport_via_transfer_assets_to_other_system_teyrchains_works() {
let amount = ASSET_HUB_PEZKUWICHAIN_ED * 100;
let native_asset: Assets = (Parent, amount).into();
let fee_asset_id: AssetId = Parent.into();
test_teyrchain_is_trusted_teleporter!(
AssetHubPezkuwichain, // Origin
vec![BridgeHubPezkuwichain], // Destinations
(native_asset, amount),
fee_asset_id,
transfer_assets
);
}
#[test]
fn teleport_via_limited_teleport_assets_from_and_to_relay() {
let amount = PEZKUWICHAIN_ED * 100;
test_relay_is_trusted_teleporter!(
Pezkuwichain,
vec![AssetHubPezkuwichain],
amount,
limited_teleport_assets
);
test_teyrchain_is_trusted_teleporter_for_relay!(
AssetHubPezkuwichain,
Pezkuwichain,
amount,
limited_teleport_assets
);
}
#[test]
fn teleport_via_transfer_assets_from_and_to_relay() {
let amount = PEZKUWICHAIN_ED * 100;
test_relay_is_trusted_teleporter!(
Pezkuwichain,
vec![AssetHubPezkuwichain],
amount,
transfer_assets
);
test_teyrchain_is_trusted_teleporter_for_relay!(
AssetHubPezkuwichain,
Pezkuwichain,
amount,
transfer_assets
);
}
/// Limited Teleport of native asset from System Teyrchain to Relay Chain
/// shouldn't work when there is not enough balance in Relay Chain's `CheckAccount`
#[test]
fn limited_teleport_native_assets_from_system_para_to_relay_fails() {
// Init values for Relay Chain
let amount_to_send: Balance = ASSET_HUB_PEZKUWICHAIN_ED * 1000;
let destination = AssetHubPezkuwichain::parent_location().into();
let beneficiary_id = PezkuwichainReceiver::get().into();
let assets = (Parent, amount_to_send).into();
let fee_asset_id: AssetId = Parent.into();
let test_args = TestContext {
sender: AssetHubPezkuwichainSender::get(),
receiver: PezkuwichainReceiver::get(),
args: TestArgs::new_para(
destination,
beneficiary_id,
amount_to_send,
assets,
None,
fee_asset_id.clone(),
),
};
let mut test = SystemParaToRelayTest::new(test_args);
let sender_balance_before = test.sender.balance;
let receiver_balance_before = test.receiver.balance;
test.set_assertion::<AssetHubPezkuwichain>(para_origin_assertions);
test.set_assertion::<Pezkuwichain>(relay_dest_assertions_fail);
test.set_dispatchable::<AssetHubPezkuwichain>(system_para_limited_teleport_assets);
test.assert();
let sender_balance_after = test.sender.balance;
let receiver_balance_after = test.receiver.balance;
let delivery_fees = AssetHubPezkuwichain::execute_with(|| {
xcm_helpers::teleport_assets_delivery_fees::<
<AssetHubPezkuwichainXcmConfig as xcm_executor::Config>::XcmSender,
>(
test.args.assets.clone(),
fee_asset_id,
test.args.weight_limit,
test.args.beneficiary,
test.args.dest,
)
});
// Sender's balance is reduced
assert_eq!(sender_balance_before - amount_to_send - delivery_fees, sender_balance_after);
// Receiver's balance does not change
assert_eq!(receiver_balance_after, receiver_balance_before);
}
/// Bidirectional teleports of local Penpal assets to Asset Hub as foreign assets while paying
/// fees using (reserve transferred) native asset.
pub fn do_bidirectional_teleport_foreign_assets_between_para_and_asset_hub_using_xt(
para_to_ah_dispatchable: fn(ParaToSystemParaTest) -> DispatchResult,
ah_to_para_dispatchable: fn(SystemParaToParaTest) -> DispatchResult,
) {
// Init values for Teyrchain
let fee_amount_to_send: Balance = ASSET_HUB_PEZKUWICHAIN_ED * 10000;
let asset_location_on_penpal =
PenpalA::execute_with(|| PenpalLocalTeleportableToAssetHub::get());
let asset_id_on_penpal = match asset_location_on_penpal.last() {
Some(Junction::GeneralIndex(id)) => *id as u32,
_ => unreachable!(),
};
let asset_amount_to_send = ASSET_HUB_PEZKUWICHAIN_ED * 1000;
let asset_owner = PenpalAssetOwner::get();
let system_para_native_asset_location = RelayLocation::get();
let sender = PenpalASender::get();
let penpal_check_account = <PenpalA as PenpalAPallet>::PezkuwiXcm::check_account();
let ah_as_seen_by_penpal = PenpalA::sibling_location_of(AssetHubPezkuwichain::para_id());
let penpal_assets: Assets = vec![
(Parent, fee_amount_to_send).into(),
(asset_location_on_penpal.clone(), asset_amount_to_send).into(),
]
.into();
let fee_asset_id: AssetId = Parent.into();
// fund Teyrchain's sender account
PenpalA::mint_foreign_asset(
<PenpalA as Chain>::RuntimeOrigin::signed(asset_owner.clone()),
system_para_native_asset_location.clone(),
sender.clone(),
fee_amount_to_send * 2,
);
// No need to create the asset (only mint) as it exists in genesis.
PenpalA::mint_asset(
<PenpalA as Chain>::RuntimeOrigin::signed(asset_owner.clone()),
asset_id_on_penpal,
sender.clone(),
asset_amount_to_send,
);
// fund Teyrchain's check account to be able to teleport
PenpalA::fund_accounts(vec![(
penpal_check_account.clone().into(),
ASSET_HUB_PEZKUWICHAIN_ED * 1000,
)]);
// prefund SA of Penpal on AssetHub with enough native tokens to pay for fees
let penpal_as_seen_by_ah = AssetHubPezkuwichain::sibling_location_of(PenpalA::para_id());
let sov_penpal_on_ah = AssetHubPezkuwichain::sovereign_account_id_of(penpal_as_seen_by_ah);
AssetHubPezkuwichain::fund_accounts(vec![(
sov_penpal_on_ah.clone().into(),
ASSET_HUB_PEZKUWICHAIN_ED * 100_000_000_000,
)]);
// Init values for System Teyrchain
let foreign_asset_at_asset_hub_pezkuwichain =
Location::new(1, [Junction::Teyrchain(PenpalA::para_id().into())])
.appended_with(asset_location_on_penpal)
.unwrap();
let penpal_to_ah_beneficiary_id = AssetHubPezkuwichainReceiver::get();
// Penpal to AH test args
let penpal_to_ah_test_args = TestContext {
sender: PenpalASender::get(),
receiver: AssetHubPezkuwichainReceiver::get(),
args: TestArgs::new_para(
ah_as_seen_by_penpal,
penpal_to_ah_beneficiary_id,
asset_amount_to_send,
penpal_assets,
Some(asset_id_on_penpal),
fee_asset_id,
),
};
let mut penpal_to_ah = ParaToSystemParaTest::new(penpal_to_ah_test_args);
let penpal_sender_balance_before = PenpalA::execute_with(|| {
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(
system_para_native_asset_location.clone(),
&PenpalASender::get(),
)
});
let ah_receiver_balance_before = penpal_to_ah.receiver.balance;
let penpal_sender_assets_before = PenpalA::execute_with(|| {
type Assets = <PenpalA as PenpalAPallet>::Assets;
<Assets as Inspect<_>>::balance(asset_id_on_penpal, &PenpalASender::get())
});
let ah_receiver_assets_before = AssetHubPezkuwichain::execute_with(|| {
type Assets = <AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::ForeignAssets;
<Assets as Inspect<_>>::balance(
foreign_asset_at_asset_hub_pezkuwichain.clone().try_into().unwrap(),
&AssetHubPezkuwichainReceiver::get(),
)
});
penpal_to_ah.set_assertion::<PenpalA>(penpal_to_ah_foreign_assets_sender_assertions);
penpal_to_ah
.set_assertion::<AssetHubPezkuwichain>(penpal_to_ah_foreign_assets_receiver_assertions);
penpal_to_ah.set_dispatchable::<PenpalA>(para_to_ah_dispatchable);
penpal_to_ah.assert();
let penpal_sender_balance_after = PenpalA::execute_with(|| {
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(
system_para_native_asset_location.clone(),
&PenpalASender::get(),
)
});
let ah_receiver_balance_after = penpal_to_ah.receiver.balance;
let penpal_sender_assets_after = PenpalA::execute_with(|| {
type Assets = <PenpalA as PenpalAPallet>::Assets;
<Assets as Inspect<_>>::balance(asset_id_on_penpal, &PenpalASender::get())
});
let ah_receiver_assets_after = AssetHubPezkuwichain::execute_with(|| {
type Assets = <AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::ForeignAssets;
<Assets as Inspect<_>>::balance(
foreign_asset_at_asset_hub_pezkuwichain.clone().try_into().unwrap(),
&AssetHubPezkuwichainReceiver::get(),
)
});
// Sender's balance is reduced
assert!(penpal_sender_balance_after < penpal_sender_balance_before);
// Receiver's balance is increased
assert!(ah_receiver_balance_after > ah_receiver_balance_before);
// Receiver's balance increased by `amount_to_send - delivery_fees - bought_execution`;
// `delivery_fees` might be paid from transfer or JIT, also `bought_execution` is unknown but
// should be non-zero
assert!(ah_receiver_balance_after < ah_receiver_balance_before + fee_amount_to_send);
// Sender's balance is reduced by exact amount
assert_eq!(penpal_sender_assets_before - asset_amount_to_send, penpal_sender_assets_after);
// Receiver's balance is increased by exact amount
assert_eq!(ah_receiver_assets_after, ah_receiver_assets_before + asset_amount_to_send);
///////////////////////////////////////////////////////////////////////
// Now test transferring foreign assets back from AssetHub to Penpal //
///////////////////////////////////////////////////////////////////////
// Move funds on AH from AHReceiver to AHSender
AssetHubPezkuwichain::execute_with(|| {
type ForeignAssets = <AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::ForeignAssets;
assert_ok!(ForeignAssets::transfer(
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(
AssetHubPezkuwichainReceiver::get()
),
foreign_asset_at_asset_hub_pezkuwichain.clone().try_into().unwrap(),
AssetHubPezkuwichainSender::get().into(),
asset_amount_to_send,
));
});
let ah_to_penpal_beneficiary_id = PenpalAReceiver::get();
let penpal_as_seen_by_ah = AssetHubPezkuwichain::sibling_location_of(PenpalA::para_id());
let ah_assets: Assets = vec![
(Parent, fee_amount_to_send).into(),
(foreign_asset_at_asset_hub_pezkuwichain.clone(), asset_amount_to_send).into(),
]
.into();
let fee_asset_id: AssetId = Parent.into();
// AH to Penpal test args
let ah_to_penpal_test_args = TestContext {
sender: AssetHubPezkuwichainSender::get(),
receiver: PenpalAReceiver::get(),
args: TestArgs::new_para(
penpal_as_seen_by_ah,
ah_to_penpal_beneficiary_id,
asset_amount_to_send,
ah_assets,
Some(asset_id_on_penpal),
fee_asset_id,
),
};
let mut ah_to_penpal = SystemParaToParaTest::new(ah_to_penpal_test_args);
let ah_sender_balance_before = ah_to_penpal.sender.balance;
let penpal_receiver_balance_before = PenpalA::execute_with(|| {
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(
system_para_native_asset_location.clone(),
&PenpalAReceiver::get(),
)
});
let ah_sender_assets_before = AssetHubPezkuwichain::execute_with(|| {
type ForeignAssets = <AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(
foreign_asset_at_asset_hub_pezkuwichain.clone().try_into().unwrap(),
&AssetHubPezkuwichainSender::get(),
)
});
let penpal_receiver_assets_before = PenpalA::execute_with(|| {
type Assets = <PenpalA as PenpalAPallet>::Assets;
<Assets as Inspect<_>>::balance(asset_id_on_penpal, &PenpalAReceiver::get())
});
ah_to_penpal
.set_assertion::<AssetHubPezkuwichain>(ah_to_penpal_foreign_assets_sender_assertions);
ah_to_penpal.set_assertion::<PenpalA>(ah_to_penpal_foreign_assets_receiver_assertions);
ah_to_penpal.set_dispatchable::<AssetHubPezkuwichain>(ah_to_para_dispatchable);
ah_to_penpal.assert();
let ah_sender_balance_after = ah_to_penpal.sender.balance;
let penpal_receiver_balance_after = PenpalA::execute_with(|| {
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(
system_para_native_asset_location,
&PenpalAReceiver::get(),
)
});
let ah_sender_assets_after = AssetHubPezkuwichain::execute_with(|| {
type ForeignAssets = <AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(
foreign_asset_at_asset_hub_pezkuwichain.try_into().unwrap(),
&AssetHubPezkuwichainSender::get(),
)
});
let penpal_receiver_assets_after = PenpalA::execute_with(|| {
type Assets = <PenpalA as PenpalAPallet>::Assets;
<Assets as Inspect<_>>::balance(asset_id_on_penpal, &PenpalAReceiver::get())
});
// Sender's balance is reduced
assert!(ah_sender_balance_after < ah_sender_balance_before);
// Receiver's balance is increased
assert!(penpal_receiver_balance_after > penpal_receiver_balance_before);
// Receiver's balance increased by `amount_to_send - delivery_fees - bought_execution`;
// `delivery_fees` might be paid from transfer or JIT, also `bought_execution` is unknown but
// should be non-zero
assert!(penpal_receiver_balance_after < penpal_receiver_balance_before + fee_amount_to_send);
// Sender's balance is reduced by exact amount
assert_eq!(ah_sender_assets_before - asset_amount_to_send, ah_sender_assets_after);
// Receiver's balance is increased by exact amount
assert_eq!(penpal_receiver_assets_after, penpal_receiver_assets_before + asset_amount_to_send);
}
/// Bidirectional teleports of local Penpal assets to Asset Hub as foreign assets should work
/// (using native reserve-based transfer for fees)
#[test]
fn bidirectional_teleport_foreign_assets_between_para_and_asset_hub() {
do_bidirectional_teleport_foreign_assets_between_para_and_asset_hub_using_xt(
para_to_system_para_transfer_assets,
system_para_to_para_transfer_assets,
);
}
/// Teleport Native Asset from AssetHub to Teyrchain fails.
#[test]
fn teleport_to_untrusted_chain_fails() {
// Init values for Teyrchain Origin
let destination = AssetHubPezkuwichain::sibling_location_of(PenpalA::para_id());
let signed_origin = <AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(
AssetHubPezkuwichainSender::get().into(),
);
let roc_to_send: Balance = PEZKUWICHAIN_ED * 10000;
let roc_location = RelayLocation::get();
// Assets to send
let assets: Vec<Asset> = vec![(roc_location.clone(), roc_to_send).into()];
let fee_id: AssetId = roc_location.into();
// this should fail
AssetHubPezkuwichain::execute_with(|| {
let result = <AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::PezkuwiXcm::transfer_assets_using_type_and_then(
signed_origin.clone(),
bx!(destination.clone().into()),
bx!(assets.clone().into()),
bx!(TransferType::Teleport),
bx!(fee_id.into()),
bx!(TransferType::Teleport),
bx!(VersionedXcm::from(Xcm::<()>::new())),
Unlimited,
);
assert_err!(
result,
DispatchError::Module(pezsp_runtime::ModuleError {
index: 31,
error: [2, 0, 0, 0],
message: Some("Filtered")
})
);
});
// this should also fail
AssetHubPezkuwichain::execute_with(|| {
let xcm: Xcm<asset_hub_pezkuwichain_runtime::RuntimeCall> = Xcm(vec![
WithdrawAsset(assets.into()),
InitiateTeleport { assets: Wild(All), dest: destination, xcm: Xcm::<()>::new() },
]);
let result = <AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::PezkuwiXcm::execute(
signed_origin,
bx!(xcm::VersionedXcm::from(xcm)),
Weight::MAX,
);
assert!(result.is_err());
});
}
@@ -0,0 +1,264 @@
// 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},
USDT_ID,
};
use pezframe_support::{
dispatch::RawOrigin,
pezsp_runtime::traits::Dispatchable,
traits::{
fungible::Inspect,
fungibles::{Inspect as FungiblesInspect, Mutate},
},
};
use pezkuwi_runtime_common::impls::VersionedLocatableAsset;
use pezkuwichain_runtime_constants::currency::GRAND;
use teyrchains_common::AccountId;
use xcm_executor::traits::ConvertLocation;
// Fund Treasury account on Asset Hub from Treasury account on Relay Chain with TYRs.
#[test]
fn spend_roc_on_asset_hub() {
// initial treasury balance on Asset Hub in TYRs.
let treasury_balance = 9_000 * GRAND;
// the balance spend on Asset Hub.
let treasury_spend_balance = 1_000 * GRAND;
let init_alice_balance = AssetHubPezkuwichain::execute_with(|| {
<<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::Balances as Inspect<_>>::balance(
&AssetHubPezkuwichain::account_id_of(ALICE),
)
});
Pezkuwichain::execute_with(|| {
type RuntimeEvent = <Pezkuwichain as Chain>::RuntimeEvent;
type RuntimeCall = <Pezkuwichain as Chain>::RuntimeCall;
type Runtime = <Pezkuwichain as Chain>::Runtime;
type Balances = <Pezkuwichain as PezkuwichainPallet>::Balances;
type Treasury = <Pezkuwichain as PezkuwichainPallet>::Treasury;
// Fund Treasury account on Asset Hub with TYRs.
let root = <Pezkuwichain 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,
));
Dmp::make_teyrchain_reachable(1000);
let native_asset = Location::here();
let asset_hub_location: Location = [Teyrchain(1000)].into();
let treasury_location: Location = (Parent, PalletInstance(18)).into();
let teleport_call = RuntimeCall::Utility(pezpallet_utility::Call::<Runtime>::dispatch_as {
as_origin: bx!(PezkuwichainOriginCaller::system(RawOrigin::Signed(treasury_account))),
call: bx!(RuntimeCall::XcmPallet(pezpallet_xcm::Call::<Runtime>::teleport_assets {
dest: bx!(VersionedLocation::from(asset_hub_location.clone())),
beneficiary: bx!(VersionedLocation::from(treasury_location)),
assets: bx!(VersionedAssets::from(Assets::from(Asset {
id: native_asset.clone().into(),
fun: treasury_balance.into()
}))),
fee_asset_id: bx!(native_asset.into()),
})),
});
// Dispatched from Root to `dispatch_as` `Signed(treasury_account)`.
assert_ok!(teleport_call.dispatch(root));
assert_expected_events!(
Pezkuwichain,
vec![
RuntimeEvent::XcmPallet(pezpallet_xcm::Event::Sent { .. }) => {},
]
);
});
Pezkuwichain::execute_with(|| {
type RuntimeEvent = <Pezkuwichain as Chain>::RuntimeEvent;
type RuntimeCall = <Pezkuwichain as Chain>::RuntimeCall;
type RuntimeOrigin = <Pezkuwichain as Chain>::RuntimeOrigin;
type Runtime = <Pezkuwichain as Chain>::Runtime;
type Treasury = <Pezkuwichain as PezkuwichainPallet>::Treasury;
// Fund Alice account from Pezkuwichain Treasury account on Asset Hub.
let treasury_origin: RuntimeOrigin =
pezkuwichain_governance::pezpallet_custom_origins::Origin::Treasurer.into();
let alice_location: Location = [Junction::AccountId32 {
network: None,
id: Pezkuwichain::account_id_of(ALICE).into(),
}]
.into();
let asset_hub_location: Location = [Teyrchain(1000)].into();
let native_asset = Location::parent();
let treasury_spend_call = RuntimeCall::Treasury(pezpallet_treasury::Call::<Runtime>::spend {
asset_kind: bx!(VersionedLocatableAsset::from((
asset_hub_location.clone(),
native_asset.into()
))),
amount: treasury_spend_balance,
beneficiary: bx!(VersionedLocation::from(alice_location)),
valid_from: None,
});
assert_ok!(treasury_spend_call.dispatch(treasury_origin));
// Claim the spend.
let bob_signed = RuntimeOrigin::signed(Pezkuwichain::account_id_of(BOB));
assert_ok!(Treasury::payout(bob_signed.clone(), 0));
assert_expected_events!(
Pezkuwichain,
vec![
RuntimeEvent::Treasury(pezpallet_treasury::Event::AssetSpendApproved { .. }) => {},
RuntimeEvent::Treasury(pezpallet_treasury::Event::Paid { .. }) => {},
]
);
});
AssetHubPezkuwichain::execute_with(|| {
type RuntimeEvent = <AssetHubPezkuwichain as Chain>::RuntimeEvent;
type Balances = <AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::Balances;
// Ensure that the funds deposited to Alice account.
let alice_account = AssetHubPezkuwichain::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!(
AssetHubPezkuwichain,
vec![
RuntimeEvent::Balances(pezpallet_balances::Event::Transfer { .. }) => {},
RuntimeEvent::TeyrchainSystem(cumulus_pallet_teyrchain_system::Event::UpwardMessageSent { .. }) => {},
RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed { success: true ,.. }) => {},
]
);
});
}
#[test]
fn create_and_claim_treasury_spend_in_usdt() {
const SPEND_AMOUNT: u128 = 10_000_000;
// treasury location from a sibling teyrchain.
let treasury_location: Location = Location::new(1, PalletInstance(18));
// treasury account on a sibling teyrchain.
let treasury_account =
ahr_xcm_config::LocationToAccountId::convert_location(&treasury_location).unwrap();
let asset_hub_location = Location::new(0, Teyrchain(AssetHubPezkuwichain::para_id().into()));
let root = <Pezkuwichain as Chain>::RuntimeOrigin::root();
// asset kind to be spent from the treasury.
let asset_kind: VersionedLocatableAsset =
(asset_hub_location, AssetId((PalletInstance(50), GeneralIndex(USDT_ID.into())).into()))
.into();
// treasury spend beneficiary.
let alice: AccountId = Pezkuwichain::account_id_of(ALICE);
let bob: AccountId = Pezkuwichain::account_id_of(BOB);
let bob_signed = <Pezkuwichain as Chain>::RuntimeOrigin::signed(bob.clone());
AssetHubPezkuwichain::execute_with(|| {
type Assets = <AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::Assets;
// USDT created at genesis, mint some assets to the treasury account.
assert_ok!(<Assets as Mutate<_>>::mint_into(USDT_ID, &treasury_account, SPEND_AMOUNT * 4));
// beneficiary has zero balance.
assert_eq!(<Assets as FungiblesInspect<_>>::balance(USDT_ID, &alice,), 0u128,);
});
Pezkuwichain::execute_with(|| {
type RuntimeEvent = <Pezkuwichain as Chain>::RuntimeEvent;
type Treasury = <Pezkuwichain as PezkuwichainPallet>::Treasury;
type AssetRate = <Pezkuwichain as PezkuwichainPallet>::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()));
Dmp::make_teyrchain_reachable(1000);
// 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!(
Pezkuwichain,
vec![
RuntimeEvent::Treasury(pezpallet_treasury::Event::Paid { .. }) => {},
]
);
});
AssetHubPezkuwichain::execute_with(|| {
type RuntimeEvent = <AssetHubPezkuwichain as Chain>::RuntimeEvent;
type Assets = <AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::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!(
AssetHubPezkuwichain,
vec![
RuntimeEvent::Assets(pezpallet_assets::Event::Transferred { asset_id: id, from, to, amount }) => {
id: id == &USDT_ID,
from: from == &treasury_account,
to: to == &alice,
amount: amount == &SPEND_AMOUNT,
},
RuntimeEvent::TeyrchainSystem(cumulus_pallet_teyrchain_system::Event::UpwardMessageSent { .. }) => {},
RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed { success: true ,.. }) => {},
]
);
// beneficiary received the assets from the treasury.
assert_eq!(<Assets as FungiblesInspect<_>>::balance(USDT_ID, &alice,), SPEND_AMOUNT,);
});
Pezkuwichain::execute_with(|| {
type RuntimeEvent = <Pezkuwichain as Chain>::RuntimeEvent;
type Treasury = <Pezkuwichain as PezkuwichainPallet>::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!(
Pezkuwichain,
vec![
RuntimeEvent::Treasury(pezpallet_treasury::Event::SpendProcessed { .. }) => {},
]
);
});
}
@@ -0,0 +1,298 @@
// 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 for XCM fee estimation in the runtime.
use crate::imports::*;
use emulated_integration_tests_common::test_can_estimate_and_pay_exact_fees;
use pezframe_support::dispatch::RawOrigin;
use xcm_runtime_apis::{
dry_run::runtime_decl_for_dry_run_api::DryRunApiV2,
fees::runtime_decl_for_xcm_payment_api::XcmPaymentApiV2,
};
fn sender_assertions(test: ParaToParaThroughAHTest) {
type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent;
PenpalA::assert_xcm_pallet_attempted_complete(None);
assert_expected_events!(
PenpalA,
vec![
RuntimeEvent::ForeignAssets(
pezpallet_assets::Event::Burned { asset_id, owner, balance }
) => {
asset_id: *asset_id == Location::new(1, []),
owner: *owner == test.sender.account_id,
balance: *balance == test.args.amount,
},
]
);
}
fn hop_assertions(test: ParaToParaThroughAHTest) {
type RuntimeEvent = <AssetHubPezkuwichain as Chain>::RuntimeEvent;
AssetHubPezkuwichain::assert_xcmp_queue_success(None);
assert_expected_events!(
AssetHubPezkuwichain,
vec![
RuntimeEvent::Balances(
pezpallet_balances::Event::Burned { amount, .. }
) => {
amount: *amount > test.args.amount * 90/100,
},
]
);
}
fn receiver_assertions(test: ParaToParaThroughAHTest) {
type RuntimeEvent = <PenpalB as Chain>::RuntimeEvent;
PenpalB::assert_xcmp_queue_success(None);
assert_expected_events!(
PenpalB,
vec![
RuntimeEvent::ForeignAssets(
pezpallet_assets::Event::Issued { asset_id, owner, .. }
) => {
asset_id: *asset_id == Location::new(1, []),
owner: *owner == test.receiver.account_id,
},
]
);
}
fn transfer_assets_para_to_para_through_ah_call(
test: ParaToParaThroughAHTest,
) -> <PenpalA as Chain>::RuntimeCall {
type RuntimeCall = <PenpalA as Chain>::RuntimeCall;
let asset_hub_location: Location =
PenpalB::sibling_location_of(AssetHubPezkuwichain::para_id());
let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset {
assets: Wild(AllCounted(test.args.assets.len() as u32)),
beneficiary: test.args.beneficiary,
}]);
RuntimeCall::PezkuwiXcm(pezpallet_xcm::Call::transfer_assets_using_type_and_then {
dest: bx!(test.args.dest.into()),
assets: bx!(test.args.assets.clone().into()),
assets_transfer_type: bx!(TransferType::RemoteReserve(asset_hub_location.clone().into())),
remote_fees_id: bx!(VersionedAssetId::from(AssetId(Location::new(1, [])))),
fees_transfer_type: bx!(TransferType::RemoteReserve(asset_hub_location.into())),
custom_xcm_on_dest: bx!(VersionedXcm::from(custom_xcm_on_dest)),
weight_limit: test.args.weight_limit,
})
}
/// We are able to dry-run and estimate the fees for a multi-hop XCM journey.
/// Scenario: Alice on PenpalA has some DOTs and wants to send them to PenpalB.
/// We want to know the fees using the `DryRunApi` and `XcmPaymentApi`.
#[test]
fn multi_hop_works() {
let destination = PenpalA::sibling_location_of(PenpalB::para_id());
let sender = PenpalASender::get();
let amount_to_send = 1_000_000_000_000;
let asset_owner = PenpalAssetOwner::get();
let assets: Assets = (Parent, amount_to_send).into();
let fee_asset_id: AssetId = Parent.into();
let relay_native_asset_location = Location::parent();
let sender_as_seen_by_ah = AssetHubPezkuwichain::sibling_location_of(PenpalA::para_id());
let sov_of_sender_on_ah =
AssetHubPezkuwichain::sovereign_account_id_of(sender_as_seen_by_ah.clone());
// fund Teyrchain's sender account
PenpalA::mint_foreign_asset(
<PenpalA as Chain>::RuntimeOrigin::signed(asset_owner.clone()),
relay_native_asset_location.clone(),
sender.clone(),
amount_to_send * 2,
);
// fund the Teyrchain Origin's SA on AssetHub with the native tokens held in reserve.
AssetHubPezkuwichain::fund_accounts(vec![(sov_of_sender_on_ah.clone(), amount_to_send * 2)]);
// Init values for Teyrchain Destination
let beneficiary_id = PenpalBReceiver::get();
let test_args = TestContext {
sender: PenpalASender::get(), // Bob in PenpalB.
receiver: PenpalBReceiver::get(), // Alice.
args: TestArgs::new_para(
destination,
beneficiary_id.clone(),
amount_to_send,
assets,
None,
fee_asset_id,
),
};
let mut test = ParaToParaThroughAHTest::new(test_args);
// We get them from the PenpalA closure.
let mut delivery_fees_amount = 0;
let mut remote_message = VersionedXcm::from(Xcm(Vec::new()));
<PenpalA as TestExt>::execute_with(|| {
type Runtime = <PenpalA as Chain>::Runtime;
type OriginCaller = <PenpalA as Chain>::OriginCaller;
let call = transfer_assets_para_to_para_through_ah_call(test.clone());
let origin = OriginCaller::system(RawOrigin::Signed(sender.clone()));
let result = Runtime::dry_run_call(origin, call, xcm::prelude::XCM_VERSION).unwrap();
// We filter the result to get only the messages we are interested in.
let (destination_to_query, messages_to_query) = &result
.forwarded_xcms
.iter()
.find(|(destination, _)| {
*destination == VersionedLocation::from(Location::new(1, [Teyrchain(1000)]))
})
.unwrap();
assert_eq!(messages_to_query.len(), 1);
remote_message = messages_to_query[0].clone();
let asset_id_for_delivery_fees = VersionedAssetId::from(Location::parent());
let delivery_fees = Runtime::query_delivery_fees(
destination_to_query.clone(),
remote_message.clone(),
asset_id_for_delivery_fees,
)
.unwrap();
delivery_fees_amount = get_amount_from_versioned_assets(delivery_fees);
});
// These are set in the AssetHub closure.
let mut intermediate_execution_fees = 0;
let mut intermediate_delivery_fees_amount = 0;
let mut intermediate_remote_message = VersionedXcm::from(Xcm::<()>(Vec::new()));
<AssetHubPezkuwichain as TestExt>::execute_with(|| {
type Runtime = <AssetHubPezkuwichain as Chain>::Runtime;
type RuntimeCall = <AssetHubPezkuwichain as Chain>::RuntimeCall;
// First we get the execution fees.
let weight = Runtime::query_xcm_weight(remote_message.clone()).unwrap();
intermediate_execution_fees = Runtime::query_weight_to_asset_fee(
weight,
VersionedAssetId::from(AssetId(Location::new(1, []))),
)
.unwrap();
// We have to do this to turn `VersionedXcm<()>` into `VersionedXcm<RuntimeCall>`.
let xcm_program = VersionedXcm::from(Xcm::<RuntimeCall>::from(
remote_message.clone().try_into().unwrap(),
));
// Now we get the delivery fees to the final destination.
let result =
Runtime::dry_run_xcm(sender_as_seen_by_ah.clone().into(), xcm_program).unwrap();
let (destination_to_query, messages_to_query) = &result
.forwarded_xcms
.iter()
.find(|(destination, _)| {
*destination == VersionedLocation::from(Location::new(1, [Teyrchain(2001)]))
})
.unwrap();
// There's actually two messages here.
// One created when the message we sent from PenpalA arrived and was executed.
// The second one when we dry-run the xcm.
// We could've gotten the message from the queue without having to dry-run, but
// offchain applications would have to dry-run, so we do it here as well.
intermediate_remote_message = messages_to_query[0].clone();
let asset_id_for_delivery_fees = VersionedAssetId::from(Location::parent());
let delivery_fees = Runtime::query_delivery_fees(
destination_to_query.clone(),
intermediate_remote_message.clone(),
asset_id_for_delivery_fees,
)
.unwrap();
intermediate_delivery_fees_amount = get_amount_from_versioned_assets(delivery_fees);
});
// Get the final execution fees in the destination.
let mut final_execution_fees = 0;
<PenpalB as TestExt>::execute_with(|| {
type Runtime = <PenpalA as Chain>::Runtime;
let weight = Runtime::query_xcm_weight(intermediate_remote_message.clone()).unwrap();
final_execution_fees = Runtime::query_weight_to_asset_fee(
weight,
VersionedAssetId::from(AssetId(Location::parent())),
)
.unwrap();
});
// Dry-running is done.
PenpalA::reset_ext();
AssetHubPezkuwichain::reset_ext();
PenpalB::reset_ext();
// Fund accounts again.
PenpalA::mint_foreign_asset(
<PenpalA as Chain>::RuntimeOrigin::signed(asset_owner),
relay_native_asset_location.clone(),
sender.clone(),
amount_to_send * 2,
);
AssetHubPezkuwichain::fund_accounts(vec![(sov_of_sender_on_ah, amount_to_send * 2)]);
// Actually run the extrinsic.
let sender_assets_before = PenpalA::execute_with(|| {
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(relay_native_asset_location.clone(), &sender)
});
let receiver_assets_before = PenpalB::execute_with(|| {
type ForeignAssets = <PenpalB as PenpalBPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(relay_native_asset_location.clone(), &beneficiary_id)
});
test.set_assertion::<PenpalA>(sender_assertions);
test.set_assertion::<AssetHubPezkuwichain>(hop_assertions);
test.set_assertion::<PenpalB>(receiver_assertions);
let call = transfer_assets_para_to_para_through_ah_call(test.clone());
test.set_call(call);
test.assert();
let sender_assets_after = PenpalA::execute_with(|| {
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(relay_native_asset_location.clone(), &sender)
});
let receiver_assets_after = PenpalB::execute_with(|| {
type ForeignAssets = <PenpalB as PenpalBPallet>::ForeignAssets;
<ForeignAssets as Inspect<_>>::balance(relay_native_asset_location, &beneficiary_id)
});
// We know the exact fees on every hop.
assert_eq!(
sender_assets_after,
sender_assets_before - amount_to_send - delivery_fees_amount /* This is charged directly
* from the sender's
* account. */
);
assert_eq!(
receiver_assets_after,
receiver_assets_before + amount_to_send -
intermediate_execution_fees -
intermediate_delivery_fees_amount -
final_execution_fees
);
}
#[test]
fn multi_hop_pay_fees_works() {
test_can_estimate_and_pay_exact_fees!(
PenpalA,
AssetHubPezkuwichain,
PenpalB,
(Parent, 1_000_000_000_000u128),
Penpal
);
}