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:
+68
@@ -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",
|
||||
]
|
||||
+111
@@ -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;
|
||||
+34
@@ -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
|
||||
);
|
||||
}
|
||||
+868
@@ -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);
|
||||
}
|
||||
+102
@@ -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 { .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
+1711
File diff suppressed because it is too large
Load Diff
+114
@@ -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"),
|
||||
}
|
||||
});
|
||||
}
|
||||
+197
@@ -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,
|
||||
},
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
+83
@@ -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, },
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
+449
@@ -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);
|
||||
}
|
||||
+643
@@ -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());
|
||||
});
|
||||
}
|
||||
+264
@@ -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 { .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
+298
@@ -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
|
||||
);
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
[package]
|
||||
name = "asset-hub-zagros-integration-tests"
|
||||
version = "1.0.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license = "Apache-2.0"
|
||||
description = "Asset Hub Zagros 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 }
|
||||
pezsp-core = { workspace = true }
|
||||
pezsp-runtime = { workspace = true }
|
||||
|
||||
# Pezkuwi
|
||||
pezpallet-xcm = { workspace = true }
|
||||
pezkuwi-runtime-common = { workspace = true, default-features = true }
|
||||
xcm = { workspace = true }
|
||||
xcm-builder = { workspace = true }
|
||||
xcm-executor = { workspace = true }
|
||||
xcm-runtime-apis = { workspace = true }
|
||||
|
||||
# Pezcumulus
|
||||
asset-test-utils = { workspace = true, default-features = true }
|
||||
pezcumulus-pezpallet-teyrchain-system = { workspace = true }
|
||||
pezcumulus-pezpallet-xcmp-queue = { workspace = true }
|
||||
emulated-integration-tests-common = { workspace = true }
|
||||
teyrchains-common = { workspace = true, default-features = true }
|
||||
zagros-system-emulated-network = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
pezsp-tracing = { workspace = true, default-features = true }
|
||||
|
||||
[features]
|
||||
runtime-benchmarks = [
|
||||
"asset-test-utils/runtime-benchmarks",
|
||||
"pezcumulus-pezpallet-teyrchain-system/runtime-benchmarks",
|
||||
"pezcumulus-pezpallet-xcmp-queue/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-xcm/runtime-benchmarks",
|
||||
"pezkuwi-runtime-common/runtime-benchmarks",
|
||||
"pezsp-runtime/runtime-benchmarks",
|
||||
"teyrchains-common/runtime-benchmarks",
|
||||
"xcm-builder/runtime-benchmarks",
|
||||
"xcm-executor/runtime-benchmarks",
|
||||
"xcm-runtime-apis/runtime-benchmarks",
|
||||
"xcm/runtime-benchmarks",
|
||||
"zagros-system-emulated-network/runtime-benchmarks",
|
||||
]
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
// 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,
|
||||
BoundedVec,
|
||||
};
|
||||
|
||||
// Pezkuwi
|
||||
pub(crate) use xcm::{
|
||||
latest::{AssetTransferFilter, 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, find_mq_processed_id, find_xcm_sent_message_id,
|
||||
get_amount_from_versioned_assets, non_fee_asset, xcm_transact_paid_execution,
|
||||
},
|
||||
xcm_simulator::helpers::TopicIdTracker,
|
||||
PenpalATeleportableAssetLocation, ASSETS_PALLET_ID, RESERVABLE_ASSET_ID, USDT_ID, XCM_V3,
|
||||
};
|
||||
pub(crate) use teyrchains_common::{AccountId, Balance};
|
||||
pub(crate) use zagros_system_emulated_network::{
|
||||
asset_hub_zagros_emulated_chain::{
|
||||
asset_hub_zagros_runtime::{
|
||||
self,
|
||||
governance::TreasuryAccount,
|
||||
xcm_config::{
|
||||
self as ahw_xcm_config, XcmConfig as AssetHubZagrosXcmConfig,
|
||||
ZagrosLocation as RelayLocation,
|
||||
},
|
||||
AssetConversionOrigin as AssetHubZagrosAssetConversionOrigin,
|
||||
ExistentialDeposit as AssetHubZagrosExistentialDeposit, ForeignAssetReserveData,
|
||||
},
|
||||
genesis::{AssetHubZagrosAssetOwner, ED as ASSET_HUB_ZAGROS_ED},
|
||||
AssetHubZagrosParaPallet as AssetHubZagrosPallet,
|
||||
},
|
||||
bridge_hub_zagros_emulated_chain::{
|
||||
bridge_hub_zagros_runtime::xcm_config::{self as bhw_xcm_config},
|
||||
BridgeHubZagrosParaPallet as BridgeHubZagrosPallet,
|
||||
},
|
||||
collectives_zagros_emulated_chain::CollectivesZagrosParaPallet as CollectivesZagrosPallet,
|
||||
coretime_zagros_emulated_chain::CoretimeZagrosParaPallet as CoretimeZagrosPallet,
|
||||
penpal_emulated_chain::{
|
||||
penpal_runtime::xcm_config::{
|
||||
CustomizableAssetFromSystemAssetHub as PenpalCustomizableAssetFromSystemAssetHub,
|
||||
LocalReservableFromAssetHub as PenpalLocalReservableFromAssetHub,
|
||||
LocalTeleportableToAssetHub as PenpalLocalTeleportableToAssetHub,
|
||||
UniversalLocation as PenpalUniversalLocation,
|
||||
UsdtFromAssetHub as PenpalUsdtFromAssetHub,
|
||||
},
|
||||
PenpalAParaPallet as PenpalAPallet, PenpalAssetOwner,
|
||||
PenpalBParaPallet as PenpalBPallet,
|
||||
},
|
||||
people_zagros_emulated_chain::PeopleZagrosParaPallet as PeopleZagrosPallet,
|
||||
zagros_emulated_chain::{
|
||||
genesis::ED as ZAGROS_ED,
|
||||
zagros_runtime::{
|
||||
governance::pezpallet_custom_origins::Origin::Treasurer,
|
||||
xcm_config::{
|
||||
UniversalLocation as ZagrosUniversalLocation, XcmConfig as ZagrosXcmConfig,
|
||||
},
|
||||
Dmp,
|
||||
},
|
||||
ZagrosRelayPallet as ZagrosPallet,
|
||||
},
|
||||
AssetHubZagrosPara as AssetHubZagros, AssetHubZagrosParaReceiver as AssetHubZagrosReceiver,
|
||||
AssetHubZagrosParaSender as AssetHubZagrosSender, BridgeHubZagrosPara as BridgeHubZagros,
|
||||
BridgeHubZagrosParaReceiver as BridgeHubZagrosReceiver,
|
||||
CollectivesZagrosPara as CollectivesZagros, CoretimeZagrosPara as CoretimeZagros,
|
||||
PenpalAPara as PenpalA, PenpalAParaReceiver as PenpalAReceiver,
|
||||
PenpalAParaSender as PenpalASender, PenpalBPara as PenpalB,
|
||||
PenpalBParaReceiver as PenpalBReceiver, PeopleZagrosPara as PeopleZagros,
|
||||
ZagrosRelay as Zagros, ZagrosRelayReceiver as ZagrosReceiver,
|
||||
ZagrosRelaySender as ZagrosSender,
|
||||
};
|
||||
|
||||
pub(crate) const ASSET_ID: u32 = 3;
|
||||
pub(crate) const ASSET_MIN_BALANCE: u128 = 1000;
|
||||
|
||||
pub(crate) type RelayToParaTest = Test<Zagros, PenpalA>;
|
||||
pub(crate) type ParaToRelayTest = Test<PenpalA, Zagros>;
|
||||
pub(crate) type RelayToSystemParaTest = Test<Zagros, AssetHubZagros>;
|
||||
pub(crate) type SystemParaToRelayTest = Test<AssetHubZagros, Zagros>;
|
||||
pub(crate) type SystemParaToParaTest = Test<AssetHubZagros, PenpalA>;
|
||||
pub(crate) type ParaToSystemParaTest = Test<PenpalA, AssetHubZagros>;
|
||||
pub(crate) type ParaToParaThroughRelayTest = Test<PenpalA, PenpalB, Zagros>;
|
||||
pub(crate) type ParaToParaThroughAHTest = Test<PenpalA, PenpalB, AssetHubZagros>;
|
||||
pub(crate) type RelayToParaThroughAHTest = Test<Zagros, PenpalA, AssetHubZagros>;
|
||||
pub(crate) type PenpalToRelayThroughAHTest = Test<PenpalA, Zagros, AssetHubZagros>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
+277
@@ -0,0 +1,277 @@
|
||||
// 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 XCM aliasing.
|
||||
|
||||
use crate::imports::*;
|
||||
use emulated_integration_tests_common::{macros::AccountId, test_cross_chain_alias};
|
||||
use pezframe_support::traits::ContainsPair;
|
||||
use xcm::latest::Junctions::*;
|
||||
|
||||
const ALLOWED: bool = true;
|
||||
const DENIED: bool = false;
|
||||
|
||||
const TELEPORT_FEES: bool = true;
|
||||
const RESERVE_TRANSFER_FEES: bool = false;
|
||||
|
||||
#[test]
|
||||
fn account_on_sibling_syschain_aliases_into_same_local_account() {
|
||||
// origin and target are the same account on different chains
|
||||
let origin: AccountId = [1; 32].into();
|
||||
let target = origin.clone();
|
||||
let fees = ZAGROS_ED * 10;
|
||||
|
||||
PenpalB::mint_foreign_asset(
|
||||
<PenpalB as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get()),
|
||||
Location::parent(),
|
||||
origin.clone(),
|
||||
fees * 10,
|
||||
);
|
||||
|
||||
// On Asset Hub we don't want to support aliasing from other chains:
|
||||
// - there is no real world demand for it, the direction is usually reversed, users already have
|
||||
// accounts on AH and want to use them cross-chain on other chains,
|
||||
// - without real world demand, it's better to keep AH permissions as tight as possible.
|
||||
// Aliasing same account doesn't work on AH.
|
||||
test_cross_chain_alias!(
|
||||
vec![
|
||||
// between BH and AH: denied
|
||||
(BridgeHubZagros, AssetHubZagros, TELEPORT_FEES, DENIED),
|
||||
// between Collectives and AH: denied
|
||||
(CollectivesZagros, AssetHubZagros, TELEPORT_FEES, DENIED),
|
||||
// between Coretime and AH: denied
|
||||
(CoretimeZagros, AssetHubZagros, TELEPORT_FEES, DENIED),
|
||||
// between People and AH: denied
|
||||
(PeopleZagros, AssetHubZagros, TELEPORT_FEES, DENIED),
|
||||
// between Penpal and AH: denied
|
||||
(PenpalB, AssetHubZagros, RESERVE_TRANSFER_FEES, DENIED)
|
||||
],
|
||||
origin,
|
||||
target,
|
||||
fees
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn account_on_sibling_syschain_cannot_alias_into_different_local_account() {
|
||||
// origin and target are different accounts on different chains
|
||||
let origin: AccountId = [1; 32].into();
|
||||
let target: AccountId = [2; 32].into();
|
||||
let fees = ZAGROS_ED * 10;
|
||||
|
||||
PenpalB::mint_foreign_asset(
|
||||
<PenpalB as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get()),
|
||||
Location::parent(),
|
||||
origin.clone(),
|
||||
fees * 10,
|
||||
);
|
||||
|
||||
// Aliasing different account on different chains
|
||||
test_cross_chain_alias!(
|
||||
vec![
|
||||
// between BH and AH: denied
|
||||
(BridgeHubZagros, AssetHubZagros, TELEPORT_FEES, DENIED),
|
||||
// between Collectives and AH: denied
|
||||
(CollectivesZagros, AssetHubZagros, TELEPORT_FEES, DENIED),
|
||||
// between Coretime and AH: denied
|
||||
(CoretimeZagros, AssetHubZagros, TELEPORT_FEES, DENIED),
|
||||
// between People and AH: denied
|
||||
(PeopleZagros, AssetHubZagros, TELEPORT_FEES, DENIED),
|
||||
// between Penpal and AH: denied
|
||||
(PenpalB, AssetHubZagros, RESERVE_TRANSFER_FEES, DENIED)
|
||||
],
|
||||
origin,
|
||||
target,
|
||||
fees
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aliasing_child_locations() {
|
||||
use AssetHubZagrosXcmConfig as XcmConfig;
|
||||
AssetHubZagros::execute_with(|| {
|
||||
// Allows aliasing descendant of origin.
|
||||
let origin = Location::new(1, X1([PalletInstance(8)].into()));
|
||||
let target = Location::new(1, X2([PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(8)].into()));
|
||||
let target = Location::new(
|
||||
1,
|
||||
X2([Teyrchain(8), AccountId32 { network: None, id: [1u8; 32] }].into()),
|
||||
);
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(8)].into()));
|
||||
let target =
|
||||
Location::new(1, X3([Teyrchain(8), PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
|
||||
// Does not allow if not descendant.
|
||||
let origin = Location::new(1, X1([PalletInstance(8)].into()));
|
||||
let target = Location::new(0, X2([PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(8)].into()));
|
||||
let target = Location::new(
|
||||
0,
|
||||
X2([Teyrchain(8), AccountId32 { network: None, id: [1u8; 32] }].into()),
|
||||
);
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(8)].into()));
|
||||
let target = Location::new(0, X1([AccountId32 { network: None, id: [1u8; 32] }].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([AccountId32 { network: None, id: [1u8; 32] }].into()));
|
||||
let target = Location::new(0, X1([AccountId32 { network: None, id: [1u8; 32] }].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn asset_hub_root_aliases_anything() {
|
||||
use AssetHubZagrosXcmConfig as XcmConfig;
|
||||
AssetHubZagros::execute_with(|| {
|
||||
// Does not allow local/AH root to alias other (non-descendant) locations.
|
||||
let origin = Location::new(0, Here);
|
||||
|
||||
let target = Location::new(1, X1([Teyrchain(2000)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(1, X1([AccountId32 { network: None, id: [1u8; 32] }].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(
|
||||
1,
|
||||
X2([Teyrchain(8), AccountId32 { network: None, id: [1u8; 32] }].into()),
|
||||
);
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target =
|
||||
Location::new(1, X3([Teyrchain(42), PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(2, X1([GlobalConsensus(Ethereum { chain_id: 1 })].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(2, X2([GlobalConsensus(Pezkuwi), Teyrchain(1000)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(1, X2([PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
|
||||
// Other AH locations cannot alias anything.
|
||||
let origin = Location::new(1, X2([Teyrchain(1000), GeneralIndex(9)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X2([Teyrchain(1000), PalletInstance(9)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(
|
||||
1,
|
||||
X2([Teyrchain(1000), AccountId32 { network: None, id: [1u8; 32] }].into()),
|
||||
);
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
|
||||
// Other root locations cannot alias anything.
|
||||
let origin = Location::new(1, Here);
|
||||
let target = Location::new(2, X1([GlobalConsensus(Ethereum { chain_id: 1 })].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(2, X2([GlobalConsensus(Pezkuwi), Teyrchain(1000)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(0, X2([PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
|
||||
let origin = Location::new(0, Here);
|
||||
let target = Location::new(1, X1([Teyrchain(2000)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(1001)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(1002)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn authorized_cross_chain_aliases() {
|
||||
// origin and target are different accounts on different chains
|
||||
let origin: AccountId = [100; 32].into();
|
||||
let bad_origin: AccountId = [150; 32].into();
|
||||
let target: AccountId = [200; 32].into();
|
||||
let fees = ZAGROS_ED * 10;
|
||||
|
||||
let pal_admin = <PenpalB as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get());
|
||||
PenpalB::mint_foreign_asset(pal_admin.clone(), Location::parent(), origin.clone(), fees * 10);
|
||||
PenpalB::mint_foreign_asset(pal_admin, Location::parent(), bad_origin.clone(), fees * 10);
|
||||
AssetHubZagros::fund_accounts(vec![(target.clone(), fees * 10)]);
|
||||
|
||||
// let's authorize `origin` on Penpal to alias `target` on AssetHub
|
||||
AssetHubZagros::execute_with(|| {
|
||||
let penpal_origin = Location::new(
|
||||
1,
|
||||
X2([
|
||||
Teyrchain(PenpalB::para_id().into()),
|
||||
AccountId32 {
|
||||
network: Some(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
id: origin.clone().into(),
|
||||
},
|
||||
]
|
||||
.into()),
|
||||
);
|
||||
// `target` adds `penpal_origin` as authorized alias
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::PezkuwiXcm::add_authorized_alias(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(target.clone()),
|
||||
Box::new(penpal_origin.into()),
|
||||
None
|
||||
));
|
||||
});
|
||||
// Verify that unauthorized `bad_origin` cannot alias into `target`, from any chain.
|
||||
test_cross_chain_alias!(
|
||||
vec![
|
||||
// between BH and AssetHub: denied
|
||||
(BridgeHubZagros, AssetHubZagros, TELEPORT_FEES, DENIED),
|
||||
// between Collectives and AssetHub: denied
|
||||
(CollectivesZagros, AssetHubZagros, TELEPORT_FEES, DENIED),
|
||||
// between People and AssetHub: denied
|
||||
(PeopleZagros, AssetHubZagros, TELEPORT_FEES, DENIED),
|
||||
// between Penpal and AssetHub: denied
|
||||
(PenpalB, AssetHubZagros, RESERVE_TRANSFER_FEES, DENIED)
|
||||
],
|
||||
bad_origin,
|
||||
target,
|
||||
fees
|
||||
);
|
||||
// Verify that only authorized `penpal::origin` can alias into `target`, while `origin` on other
|
||||
// chains cannot.
|
||||
test_cross_chain_alias!(
|
||||
vec![
|
||||
// between BH and AssetHub: denied
|
||||
(BridgeHubZagros, AssetHubZagros, TELEPORT_FEES, DENIED),
|
||||
// between Collectives and AssetHub: denied
|
||||
(CollectivesZagros, AssetHubZagros, TELEPORT_FEES, DENIED),
|
||||
// between People and AssetHub: denied
|
||||
(PeopleZagros, AssetHubZagros, TELEPORT_FEES, DENIED),
|
||||
// between Penpal and AssetHub: allowed
|
||||
(PenpalB, AssetHubZagros, RESERVE_TRANSFER_FEES, ALLOWED)
|
||||
],
|
||||
origin,
|
||||
target,
|
||||
fees
|
||||
);
|
||||
// remove authorization for `origin` on Penpal to alias `target` on AssetHub
|
||||
AssetHubZagros::execute_with(|| {
|
||||
// `target` removes all authorized aliases
|
||||
assert_ok!(
|
||||
<AssetHubZagros as AssetHubZagrosPallet>::PezkuwiXcm::remove_all_authorized_aliases(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(target.clone())
|
||||
)
|
||||
);
|
||||
});
|
||||
// Verify `penpal::origin` can no longer alias into `target` on AssetHub.
|
||||
test_cross_chain_alias!(
|
||||
vec![(PenpalB, AssetHubZagros, RESERVE_TRANSFER_FEES, DENIED)],
|
||||
origin,
|
||||
target,
|
||||
fees
|
||||
);
|
||||
}
|
||||
+34
@@ -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 = AssetHubZagrosExistentialDeposit::get();
|
||||
let assets: Assets = (Parent, amount).into();
|
||||
|
||||
test_chain_can_claim_assets!(
|
||||
AssetHubZagros,
|
||||
RuntimeCall,
|
||||
NetworkId::ByGenesis(ZAGROS_GENESIS_HASH),
|
||||
assets,
|
||||
amount
|
||||
);
|
||||
}
|
||||
+327
@@ -0,0 +1,327 @@
|
||||
// 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::{
|
||||
assets_balance_on, create_pool_with_wnd_on, foreign_balance_on,
|
||||
imports::{
|
||||
asset_hub_zagros_runtime::{ExistentialDeposit, Runtime},
|
||||
*,
|
||||
},
|
||||
};
|
||||
use asset_hub_zagros_runtime::{
|
||||
xcm_config::ZagrosLocation, Balances, ForeignAssets, PezkuwiXcm, RuntimeOrigin,
|
||||
};
|
||||
use emulated_integration_tests_common::{accounts::ALICE, xcm_emulator::TestExt};
|
||||
use pezframe_support::{
|
||||
assert_err_ignore_postinfo, assert_ok,
|
||||
traits::fungible::{Inspect, Mutate},
|
||||
};
|
||||
use pezsp_tracing::capture_test_logs;
|
||||
use std::convert::Into;
|
||||
use teyrchains_common::{AccountId, Balance};
|
||||
use xcm::latest::{Assets, Error as XcmError, Location, Xcm};
|
||||
|
||||
const UNITS: Balance = 1_000_000_000;
|
||||
|
||||
#[test]
|
||||
fn exchange_asset_success() {
|
||||
test_exchange_asset(true, 500 * UNITS, 665 * UNITS, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exchange_asset_insufficient_liquidity() {
|
||||
let log_capture = capture_test_logs!({
|
||||
test_exchange_asset(
|
||||
true,
|
||||
1_000 * UNITS,
|
||||
2_000 * UNITS,
|
||||
Some(InstructionError { index: 1, error: XcmError::NoDeal }),
|
||||
);
|
||||
});
|
||||
assert!(log_capture.contains("NoDeal"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exchange_asset_insufficient_balance() {
|
||||
let log_capture = capture_test_logs!({
|
||||
test_exchange_asset(
|
||||
true,
|
||||
5_000 * UNITS,
|
||||
1_665 * UNITS,
|
||||
Some(InstructionError { index: 0, error: XcmError::FailedToTransactAsset("") }),
|
||||
);
|
||||
});
|
||||
assert!(log_capture.contains("Funds are unavailable"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exchange_asset_pool_not_created() {
|
||||
test_exchange_asset(
|
||||
false,
|
||||
500 * UNITS,
|
||||
665 * UNITS,
|
||||
Some(InstructionError { index: 1, error: XcmError::NoDeal }),
|
||||
);
|
||||
}
|
||||
|
||||
fn test_exchange_asset(
|
||||
create_pool: bool,
|
||||
give_amount: Balance,
|
||||
want_amount: Balance,
|
||||
expected_error: Option<InstructionError>,
|
||||
) {
|
||||
let alice: AccountId = Zagros::account_id_of(ALICE);
|
||||
let native_asset_location = ZagrosLocation::get();
|
||||
let native_asset_id = AssetId(native_asset_location.clone());
|
||||
let origin = RuntimeOrigin::signed(alice.clone());
|
||||
let asset_location = Location::new(1, [Teyrchain(2001)]);
|
||||
let asset_id = AssetId(asset_location.clone());
|
||||
|
||||
// Setup initial state
|
||||
AssetHubZagros::execute_with(|| {
|
||||
assert_ok!(<Balances as Mutate<_>>::mint_into(
|
||||
&alice,
|
||||
ExistentialDeposit::get() + (1_000 * UNITS)
|
||||
));
|
||||
|
||||
assert_ok!(ForeignAssets::force_create(
|
||||
RuntimeOrigin::root(),
|
||||
asset_location.clone().into(),
|
||||
alice.clone().into(),
|
||||
true,
|
||||
1
|
||||
));
|
||||
});
|
||||
|
||||
if create_pool {
|
||||
create_pool_with_wnd_on!(AssetHubZagros, asset_location.clone(), true, alice.clone());
|
||||
}
|
||||
|
||||
// Execute and verify swap
|
||||
AssetHubZagros::execute_with(|| {
|
||||
let foreign_balance_before = ForeignAssets::balance(asset_location.clone(), &alice);
|
||||
let wnd_balance_before = Balances::total_balance(&alice);
|
||||
|
||||
let give: Assets = (native_asset_id, give_amount).into();
|
||||
let want: Assets = (asset_id, want_amount).into();
|
||||
let xcm = Xcm(vec![
|
||||
WithdrawAsset(give.clone().into()),
|
||||
ExchangeAsset { give: give.into(), want: want.into(), maximal: true },
|
||||
DepositAsset { assets: Wild(All), beneficiary: alice.clone().into() },
|
||||
]);
|
||||
|
||||
let result = PezkuwiXcm::execute(origin, bx!(xcm::VersionedXcm::from(xcm)), Weight::MAX);
|
||||
|
||||
let foreign_balance_after = ForeignAssets::balance(asset_location, &alice);
|
||||
let wnd_balance_after = Balances::total_balance(&alice);
|
||||
|
||||
if let Some(InstructionError { index, error }) = expected_error {
|
||||
assert_err_ignore_postinfo!(
|
||||
result,
|
||||
pezpallet_xcm::Error::<Runtime>::LocalExecutionIncompleteWithError {
|
||||
index,
|
||||
error: error.into()
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
foreign_balance_after, foreign_balance_before,
|
||||
"Foreign balance changed unexpectedly: got {foreign_balance_after}, expected {foreign_balance_before}"
|
||||
);
|
||||
assert_eq!(
|
||||
wnd_balance_after, wnd_balance_before,
|
||||
"ZGR balance changed unexpectedly: got {wnd_balance_after}, expected {wnd_balance_before}"
|
||||
);
|
||||
} else {
|
||||
assert_ok!(result);
|
||||
assert!(
|
||||
foreign_balance_after >= foreign_balance_before + want_amount,
|
||||
"Expected foreign balance to increase by at least {want_amount} units, got {foreign_balance_after} from {foreign_balance_before}"
|
||||
);
|
||||
assert_eq!(
|
||||
wnd_balance_after, wnd_balance_before - give_amount,
|
||||
"Expected ZGR balance to decrease by {give_amount} units, got {wnd_balance_after} from {wnd_balance_before}"
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exchange_asset_from_penpal_via_asset_hub_back_to_penpal() {
|
||||
let sender = PenpalASender::get();
|
||||
let sov_of_penpal_on_asset_hub = AssetHubZagros::sovereign_account_id_of(
|
||||
AssetHubZagros::sibling_location_of(PenpalA::para_id()),
|
||||
);
|
||||
let wnd_from_teyrchain_pov: Location = RelayLocation::get();
|
||||
let usdt_asset_hub_pov =
|
||||
Location::new(0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(USDT_ID.into())]);
|
||||
let usdt_penpal_pov = PenpalUsdtFromAssetHub::get();
|
||||
let amount_of_wnd_to_transfer_to_ah = ZAGROS_ED * 1_000_000_000;
|
||||
let amount_of_usdt_we_want_from_exchange = 1_000_000_000;
|
||||
|
||||
let mut topic_id_tracker = TopicIdTracker::new();
|
||||
|
||||
// SA-of-Penpal-on-AHW should contain ZGR amount equal at least the amount that will be
|
||||
// transferred-in to AH Since AH is the reserve for ZGR
|
||||
AssetHubZagros::fund_accounts(vec![(
|
||||
sov_of_penpal_on_asset_hub.clone().into(),
|
||||
ASSET_HUB_ZAGROS_ED + amount_of_wnd_to_transfer_to_ah,
|
||||
)]);
|
||||
// Give the sender enough ZGR
|
||||
PenpalA::mint_foreign_asset(
|
||||
<PenpalA as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get()),
|
||||
wnd_from_teyrchain_pov.clone(),
|
||||
sender.clone(),
|
||||
amount_of_wnd_to_transfer_to_ah,
|
||||
);
|
||||
|
||||
// We create a pool between ZGR and USDT in AssetHub so we can do the exchange
|
||||
create_pool_with_wnd_on!(
|
||||
AssetHubZagros,
|
||||
usdt_asset_hub_pov.clone(),
|
||||
false,
|
||||
AssetHubZagrosSender::get(),
|
||||
1_000_000_000_000,
|
||||
20_000_000_000
|
||||
);
|
||||
|
||||
// Query initial balances
|
||||
let sender_usdt_on_penpal_before =
|
||||
foreign_balance_on!(PenpalA, usdt_penpal_pov.clone(), &sender);
|
||||
let sender_usdt_on_ah_before = assets_balance_on!(AssetHubZagros, USDT_ID, &sender);
|
||||
|
||||
let asset_hub_location_penpal_pov = PenpalA::sibling_location_of(AssetHubZagros::para_id());
|
||||
let penpal_location_ah_pov = AssetHubZagros::sibling_location_of(PenpalA::para_id());
|
||||
|
||||
PenpalA::execute_with(|| {
|
||||
let sender_signed_origin = <PenpalA as Chain>::RuntimeOrigin::signed(sender.clone());
|
||||
|
||||
let local_fees_amount = 80_000_000_000_000u128;
|
||||
let remote_fees_amount = 200_000_000_000_000u128;
|
||||
|
||||
let penpal_local_fees: Asset = (wnd_from_teyrchain_pov.clone(), local_fees_amount).into();
|
||||
let ah_remote_fees: Asset = (wnd_from_teyrchain_pov.clone(), remote_fees_amount).into();
|
||||
let penpal_remote_fees: Asset = (wnd_from_teyrchain_pov.clone(), remote_fees_amount).into();
|
||||
let wnd_to_withdraw: Asset =
|
||||
(wnd_from_teyrchain_pov.clone(), amount_of_wnd_to_transfer_to_ah).into();
|
||||
|
||||
// xcm to be executed by penpal, sent by ah
|
||||
let xcm_back_on_penpal = Xcm(vec![
|
||||
RefundSurplus,
|
||||
DepositAsset { assets: Wild(All), beneficiary: sender.clone().into() },
|
||||
]);
|
||||
// xcm to be executed by ah, sent by penpal
|
||||
let xcm_on_ah = Xcm(vec![
|
||||
ExchangeAsset {
|
||||
give: Definite((wnd_from_teyrchain_pov.clone(), 100_000_000_000u128).into()),
|
||||
want: (usdt_asset_hub_pov.clone(), amount_of_usdt_we_want_from_exchange).into(),
|
||||
maximal: false,
|
||||
},
|
||||
InitiateTransfer {
|
||||
destination: penpal_location_ah_pov,
|
||||
remote_fees: Some(AssetTransferFilter::ReserveDeposit(
|
||||
penpal_remote_fees.clone().into(),
|
||||
)),
|
||||
preserve_origin: false,
|
||||
assets: BoundedVec::truncate_from(vec![AssetTransferFilter::ReserveDeposit(Wild(
|
||||
All,
|
||||
))]),
|
||||
remote_xcm: xcm_back_on_penpal,
|
||||
},
|
||||
RefundSurplus,
|
||||
DepositAsset { assets: Wild(All), beneficiary: sender.clone().into() },
|
||||
]);
|
||||
// xcm to be executed locally on penpal as starting point
|
||||
let xcm = Xcm::<()>(vec![
|
||||
WithdrawAsset(wnd_to_withdraw.into()),
|
||||
PayFees { asset: penpal_local_fees },
|
||||
InitiateTransfer {
|
||||
destination: asset_hub_location_penpal_pov,
|
||||
remote_fees: Some(AssetTransferFilter::ReserveWithdraw(
|
||||
ah_remote_fees.clone().into(),
|
||||
)),
|
||||
preserve_origin: false,
|
||||
assets: BoundedVec::truncate_from(vec![AssetTransferFilter::ReserveWithdraw(
|
||||
Wild(All),
|
||||
)]),
|
||||
remote_xcm: xcm_on_ah,
|
||||
},
|
||||
RefundSurplus,
|
||||
DepositAsset { assets: Wild(All), beneficiary: sender.clone().into() },
|
||||
]);
|
||||
// initiate transaction
|
||||
<PenpalA as PenpalAPallet>::PezkuwiXcm::execute(
|
||||
sender_signed_origin,
|
||||
bx!(xcm::VersionedXcm::from(xcm.into())),
|
||||
Weight::MAX,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// verify expected events;
|
||||
PenpalA::assert_xcm_pallet_attempted_complete(None);
|
||||
|
||||
let msg_sent_id = find_xcm_sent_message_id::<PenpalA>().expect("Missing Sent Event");
|
||||
topic_id_tracker.insert("PenpalA_sent", msg_sent_id.into());
|
||||
});
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
RuntimeEvent::AssetConversion(
|
||||
pezpallet_asset_conversion::Event::SwapCreditExecuted { amount_out, ..}
|
||||
) => { amount_out: *amount_out == amount_of_usdt_we_want_from_exchange, },
|
||||
]
|
||||
);
|
||||
|
||||
let mq_prc_id = find_mq_processed_id::<AssetHubZagros>().expect("Missing Processed Event");
|
||||
topic_id_tracker.insert("AssetHubZagros_received", mq_prc_id);
|
||||
let msg_sent_id = find_xcm_sent_message_id::<AssetHubZagros>().expect("Missing Sent Event");
|
||||
topic_id_tracker.insert("AssetHubZagros_sent", msg_sent_id.into());
|
||||
});
|
||||
|
||||
PenpalA::execute_with(|| {
|
||||
type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
PenpalA,
|
||||
vec![
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
|
||||
let mq_prc_id = find_mq_processed_id::<PenpalA>().expect("Missing Processed Event");
|
||||
topic_id_tracker.insert("PenpalA_received", mq_prc_id);
|
||||
});
|
||||
|
||||
topic_id_tracker.assert_unique();
|
||||
|
||||
// Query final balances
|
||||
let sender_usdt_on_ah_after = assets_balance_on!(AssetHubZagros, USDT_ID, &sender);
|
||||
let sender_usdt_on_penpal_after =
|
||||
foreign_balance_on!(PenpalA, usdt_penpal_pov.clone(), &sender);
|
||||
|
||||
// Receiver's balance is increased by usdt amount we got from exchange
|
||||
assert_eq!(
|
||||
sender_usdt_on_penpal_after,
|
||||
sender_usdt_on_penpal_before + amount_of_usdt_we_want_from_exchange
|
||||
);
|
||||
// Usdt amount on senders account AH side should stay the same i.e. all usdt came from exchange
|
||||
// not free balance
|
||||
assert_eq!(sender_usdt_on_ah_before, sender_usdt_on_ah_after);
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
// 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::traits::fungibles::{Inspect, Mutate};
|
||||
use pezkuwi_runtime_common::impls::VersionedLocatableAsset;
|
||||
use xcm_executor::traits::ConvertLocation;
|
||||
|
||||
#[test]
|
||||
fn create_and_claim_treasury_spend() {
|
||||
const SPEND_AMOUNT: u128 = 1_000_000_000;
|
||||
// treasury location from a sibling teyrchain.
|
||||
let treasury_location: Location =
|
||||
Location::new(1, [Teyrchain(CollectivesZagros::para_id().into()), PalletInstance(65)]);
|
||||
// treasury account on a sibling teyrchain.
|
||||
let treasury_account =
|
||||
ahw_xcm_config::LocationToAccountId::convert_location(&treasury_location).unwrap();
|
||||
let asset_hub_location = Location::new(1, [Teyrchain(AssetHubZagros::para_id().into())]);
|
||||
let root = <CollectivesZagros 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 = Zagros::account_id_of(ALICE);
|
||||
let bob: AccountId = CollectivesZagros::account_id_of(BOB);
|
||||
let bob_signed = <CollectivesZagros as Chain>::RuntimeOrigin::signed(bob.clone());
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type Assets = <AssetHubZagros as AssetHubZagrosPallet>::Assets;
|
||||
|
||||
// USDT created at genesis, mint some assets to the fellowship treasury account.
|
||||
assert_ok!(<Assets as Mutate<_>>::mint_into(USDT_ID, &treasury_account, SPEND_AMOUNT * 4));
|
||||
// beneficiary has zero balance.
|
||||
assert_eq!(<Assets as Inspect<_>>::balance(USDT_ID, &alice,), 0u128,);
|
||||
});
|
||||
|
||||
CollectivesZagros::execute_with(|| {
|
||||
type RuntimeEvent = <CollectivesZagros as Chain>::RuntimeEvent;
|
||||
type FellowshipTreasury =
|
||||
<CollectivesZagros as CollectivesZagrosPallet>::FellowshipTreasury;
|
||||
type AssetRate = <CollectivesZagros as CollectivesZagrosPallet>::AssetRate;
|
||||
|
||||
// create a conversion rate from `asset_kind` to the native currency.
|
||||
assert_ok!(AssetRate::create(root.clone(), Box::new(asset_kind.clone()), 2.into()));
|
||||
|
||||
// create and approve a treasury spend.
|
||||
assert_ok!(FellowshipTreasury::spend(
|
||||
root,
|
||||
Box::new(asset_kind),
|
||||
SPEND_AMOUNT,
|
||||
Box::new(Location::new(0, Into::<[u8; 32]>::into(alice.clone())).into()),
|
||||
None,
|
||||
));
|
||||
// claim the spend.
|
||||
assert_ok!(FellowshipTreasury::payout(bob_signed.clone(), 0));
|
||||
|
||||
assert_expected_events!(
|
||||
CollectivesZagros,
|
||||
vec![
|
||||
RuntimeEvent::FellowshipTreasury(pezpallet_treasury::Event::Paid { .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
type Assets = <AssetHubZagros as AssetHubZagrosPallet>::Assets;
|
||||
|
||||
// assert events triggered by xcm pay program
|
||||
// 1. treasury asset transferred to spend beneficiary
|
||||
// 2. response to the Fellowship treasury pallet instance sent back
|
||||
// 3. XCM program completed
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
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::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},
|
||||
RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed { success: true ,.. }) => {},
|
||||
]
|
||||
);
|
||||
// beneficiary received the assets from the treasury.
|
||||
assert_eq!(<Assets as Inspect<_>>::balance(USDT_ID, &alice,), SPEND_AMOUNT,);
|
||||
});
|
||||
|
||||
CollectivesZagros::execute_with(|| {
|
||||
type RuntimeEvent = <CollectivesZagros as Chain>::RuntimeEvent;
|
||||
type FellowshipTreasury =
|
||||
<CollectivesZagros as CollectivesZagrosPallet>::FellowshipTreasury;
|
||||
|
||||
// check the payment status to ensure the response from the AssetHub was received.
|
||||
assert_ok!(FellowshipTreasury::check_status(bob_signed, 0));
|
||||
assert_expected_events!(
|
||||
CollectivesZagros,
|
||||
vec![
|
||||
RuntimeEvent::FellowshipTreasury(pezpallet_treasury::Event::SpendProcessed { .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
+538
@@ -0,0 +1,538 @@
|
||||
// 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::{
|
||||
assets_balance_on, create_pool_with_wnd_on, foreign_balance_on, imports::*,
|
||||
tests::send::penpal_register_foreign_asset_on_asset_hub,
|
||||
};
|
||||
|
||||
// Registers a new asset on Penpal, then registers it over XCM as foreign asset on Asset Hub.
|
||||
// The foreign asset is set up either as teleportable between Penpal and AH, by making AH a reserve
|
||||
// for it too. Or it keeps the asset's reserve solely on Penpal resulting in reserve-based transfers
|
||||
// between Penpal and AH.
|
||||
pub fn set_up_foreign_asset(
|
||||
sender: pezsp_runtime::AccountId32,
|
||||
asset_id_on_penpal: u32,
|
||||
asset_amount_to_send: u128,
|
||||
teleportable: bool,
|
||||
) -> (Location, Location) {
|
||||
let asset_owner = PenpalAssetOwner::get();
|
||||
|
||||
// Give the sender enough native
|
||||
PenpalA::mint_foreign_asset(
|
||||
<PenpalA as Chain>::RuntimeOrigin::signed(asset_owner.clone()),
|
||||
RelayLocation::get(),
|
||||
sender.clone(),
|
||||
asset_amount_to_send,
|
||||
);
|
||||
|
||||
// Create the asset on Penpal
|
||||
let to_fund = asset_amount_to_send * 2;
|
||||
PenpalA::force_create_asset(
|
||||
asset_id_on_penpal,
|
||||
asset_owner.clone(),
|
||||
true,
|
||||
ASSET_MIN_BALANCE,
|
||||
vec![(sender.clone(), to_fund)],
|
||||
);
|
||||
PenpalA::execute_with(|| {
|
||||
type Assets = <PenpalA as PenpalAPallet>::Assets;
|
||||
assert!(Assets::asset_exists(asset_id_on_penpal));
|
||||
});
|
||||
let asset_location_on_penpal = Location::new(
|
||||
0,
|
||||
[
|
||||
Junction::PalletInstance(ASSETS_PALLET_ID),
|
||||
Junction::GeneralIndex(asset_id_on_penpal.into()),
|
||||
],
|
||||
);
|
||||
|
||||
// Setup a pool on Penpal between native asset and newly created asset, so we can pay fees using
|
||||
// new asset directly.
|
||||
create_pool_with_wnd_on!(PenpalA, asset_location_on_penpal.clone(), false, asset_owner.clone());
|
||||
|
||||
// Register asset on Asset Hub using XCM
|
||||
let penpal_sovereign_account = AssetHubZagros::sovereign_account_id_of(
|
||||
AssetHubZagros::sibling_location_of(PenpalA::para_id()),
|
||||
);
|
||||
let penpal_location = Location::new(1, [Junction::Teyrchain(PenpalA::para_id().into())]);
|
||||
let foreign_asset_at_asset_hub =
|
||||
penpal_location.clone().appended_with(asset_location_on_penpal.clone()).unwrap();
|
||||
// Do remote registration
|
||||
penpal_register_foreign_asset_on_asset_hub(asset_location_on_penpal.clone());
|
||||
|
||||
// Setup a pool on Asset Hub between native asset and newly created asset, so we can pay fees
|
||||
// using new asset directly.
|
||||
create_pool_with_wnd_on!(
|
||||
AssetHubZagros,
|
||||
foreign_asset_at_asset_hub.clone(),
|
||||
true,
|
||||
penpal_sovereign_account.clone()
|
||||
);
|
||||
|
||||
if teleportable {
|
||||
// Configure Penpal to allow teleports of this asset to AH
|
||||
PenpalA::execute_with(|| {
|
||||
assert_ok!(<PenpalA as Chain>::System::set_storage(
|
||||
<PenpalA as Chain>::RuntimeOrigin::root(),
|
||||
vec![(
|
||||
PenpalLocalTeleportableToAssetHub::key().to_vec(),
|
||||
asset_location_on_penpal.encode(),
|
||||
)],
|
||||
));
|
||||
});
|
||||
}
|
||||
let reserves_data = vec![(penpal_location, teleportable).into()];
|
||||
AssetHubZagros::set_foreign_asset_reserves(
|
||||
foreign_asset_at_asset_hub.clone(),
|
||||
penpal_sovereign_account.clone(),
|
||||
reserves_data,
|
||||
);
|
||||
(asset_location_on_penpal, foreign_asset_at_asset_hub)
|
||||
}
|
||||
|
||||
// Helper for Penpal root to call ForeignAssets::set_reserves() on Asset Hub.
|
||||
pub fn penpal_set_foreign_asset_reserves_on_asset_hub(
|
||||
asset_id_on_ah: Location,
|
||||
reserves: Vec<ForeignAssetReserveData>,
|
||||
) {
|
||||
// Encoded `set_reserves` call to be executed in AssetHub
|
||||
let call = <AssetHubZagros as Chain>::RuntimeCall::ForeignAssets(pezpallet_assets::Call::<
|
||||
<AssetHubZagros as Chain>::Runtime,
|
||||
pezpallet_assets::Instance2,
|
||||
>::set_reserves {
|
||||
id: asset_id_on_ah.into(),
|
||||
reserves,
|
||||
})
|
||||
.encode()
|
||||
.into();
|
||||
let penpal_sovereign = AssetHubZagros::sovereign_account_id_of(
|
||||
AssetHubZagros::sibling_location_of(PenpalA::para_id()),
|
||||
);
|
||||
let origin_kind = OriginKind::Xcm;
|
||||
let fee_amount = ASSET_HUB_ZAGROS_ED * 1000000;
|
||||
let system_asset = (Parent, fee_amount).into();
|
||||
let root_origin = <PenpalA as Chain>::RuntimeOrigin::root();
|
||||
let asset_hub_location = PenpalA::sibling_location_of(AssetHubZagros::para_id()).into();
|
||||
let xcm =
|
||||
xcm_transact_paid_execution(call, origin_kind, system_asset, penpal_sovereign.clone());
|
||||
|
||||
PenpalA::execute_with(|| {
|
||||
assert_ok!(<PenpalA as PenpalAPallet>::PezkuwiXcm::send(
|
||||
root_origin,
|
||||
bx!(asset_hub_location),
|
||||
bx!(xcm),
|
||||
));
|
||||
PenpalA::assert_xcm_pallet_sent();
|
||||
});
|
||||
}
|
||||
|
||||
// ==============================================================================================
|
||||
// ==== Bidirectional Transfer - Teleportable Foreign Asset - Penpal<->AssetHub ====
|
||||
// ==============================================================================================
|
||||
/// Transfers of teleportable foreign asset from Penpal to AssetHub and back.
|
||||
/// Also verifies that reserve-transferring the asset fails both ways.
|
||||
#[test]
|
||||
fn bidirectional_teleport_foreign_asset_between_penpal_and_asset_hub() {
|
||||
let sender = PenpalASender::get();
|
||||
let receiver = AssetHubZagrosReceiver::get();
|
||||
let new_asset_id = 42;
|
||||
let asset_amount_to_send = ASSET_HUB_ZAGROS_ED * 10_000;
|
||||
let (asset_location_on_penpal, foreign_asset_location_on_ah) =
|
||||
set_up_foreign_asset(sender.clone(), new_asset_id, asset_amount_to_send, true);
|
||||
|
||||
////////////////////////////////
|
||||
// Teleport it from Penpal to AH
|
||||
////////////////////////////////
|
||||
|
||||
let penpal_sender_balance_before = assets_balance_on!(PenpalA, new_asset_id, &sender);
|
||||
let ah_receiver_balance_before =
|
||||
foreign_balance_on!(AssetHubZagros, foreign_asset_location_on_ah.clone(), &receiver);
|
||||
|
||||
let dest = PenpalA::sibling_location_of(AssetHubZagros::para_id());
|
||||
let assets: Assets =
|
||||
vec![(asset_location_on_penpal.clone(), asset_amount_to_send).into()].into();
|
||||
// execute xcm from penpal to asset hub
|
||||
PenpalA::execute_with(|| {
|
||||
// xcm to be executed at dest
|
||||
let xcm_on_dest = Xcm(vec![
|
||||
// since this is the last hop, we don't need to further use any assets previously
|
||||
// reserved for fees (there are no further hops to cover delivery fees for); we
|
||||
// RefundSurplus to get back any unspent fees
|
||||
RefundSurplus,
|
||||
DepositAsset { assets: Wild(All), beneficiary: receiver.clone().into() },
|
||||
]);
|
||||
let xcm = Xcm::<()>(vec![
|
||||
WithdrawAsset(assets.clone().into()),
|
||||
SetFeesMode { jit_withdraw: true },
|
||||
InitiateTransfer {
|
||||
destination: dest.clone(),
|
||||
remote_fees: Some(AssetTransferFilter::Teleport(assets.clone().into())),
|
||||
preserve_origin: false,
|
||||
assets: BoundedVec::new(),
|
||||
remote_xcm: xcm_on_dest.clone(),
|
||||
},
|
||||
]);
|
||||
// teleporting the asset works
|
||||
<PenpalA as PenpalAPallet>::PezkuwiXcm::execute(
|
||||
<PenpalA as Chain>::RuntimeOrigin::signed(sender.clone()),
|
||||
bx!(xcm::VersionedXcm::from(xcm.into())),
|
||||
Weight::MAX,
|
||||
)
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
let penpal_sender_balance_after = assets_balance_on!(PenpalA, new_asset_id, &sender);
|
||||
let ah_receiver_balance_after =
|
||||
foreign_balance_on!(AssetHubZagros, foreign_asset_location_on_ah.clone(), &receiver);
|
||||
|
||||
assert!(penpal_sender_balance_after < penpal_sender_balance_before);
|
||||
assert!(ah_receiver_balance_after > ah_receiver_balance_before);
|
||||
|
||||
// reserve-transferring the asset fails
|
||||
PenpalA::execute_with(|| {
|
||||
let xcm = Xcm::<()>(vec![
|
||||
WithdrawAsset(assets.clone().into()),
|
||||
SetFeesMode { jit_withdraw: true },
|
||||
InitiateTransfer {
|
||||
destination: dest,
|
||||
remote_fees: Some(AssetTransferFilter::ReserveDeposit(assets.clone().into())),
|
||||
preserve_origin: false,
|
||||
assets: BoundedVec::new(),
|
||||
remote_xcm: Default::default(),
|
||||
},
|
||||
]);
|
||||
<PenpalA as PenpalAPallet>::PezkuwiXcm::execute(
|
||||
<PenpalA as Chain>::RuntimeOrigin::signed(sender.clone()),
|
||||
bx!(xcm::VersionedXcm::from(xcm.into())),
|
||||
Weight::MAX,
|
||||
)
|
||||
.unwrap();
|
||||
});
|
||||
// AH is expected to reject the transfer with `UntrustedReserveLocation`
|
||||
let expected_origin = AssetHubZagros::sibling_location_of(PenpalA::para_id());
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::PezkuwiXcm(
|
||||
pezpallet_xcm::Event::ProcessXcmError { origin, error, .. }
|
||||
) => {
|
||||
origin: *origin == expected_origin,
|
||||
error: *error == xcm::latest::Error::UntrustedReserveLocation,
|
||||
},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
/////////////////////////////////////
|
||||
// Teleport it back from AH to Penpal
|
||||
/////////////////////////////////////
|
||||
|
||||
let asset_amount_to_send = ah_receiver_balance_after;
|
||||
let ah_sender_balance_before =
|
||||
foreign_balance_on!(AssetHubZagros, foreign_asset_location_on_ah.clone(), &receiver);
|
||||
let penpal_receiver_balance_before = assets_balance_on!(PenpalA, new_asset_id, &sender);
|
||||
|
||||
let dest = AssetHubZagros::sibling_location_of(PenpalA::para_id());
|
||||
// execute xcm from asset hub to penpal
|
||||
AssetHubZagros::execute_with(|| {
|
||||
let assets: Assets =
|
||||
vec![(foreign_asset_location_on_ah.clone(), asset_amount_to_send).into()].into();
|
||||
// xcm to be executed at dest
|
||||
let xcm_on_dest = Xcm(vec![
|
||||
// since this is the last hop, we don't need to further use any assets previously
|
||||
// reserved for fees (there are no further hops to cover delivery fees for); we
|
||||
// RefundSurplus to get back any unspent fees
|
||||
RefundSurplus,
|
||||
DepositAsset { assets: Wild(All), beneficiary: sender.clone().into() },
|
||||
]);
|
||||
// reserve-transferring the asset back to penpal fails
|
||||
let xcm = Xcm::<()>(vec![
|
||||
WithdrawAsset(assets.clone().into()),
|
||||
SetFeesMode { jit_withdraw: true },
|
||||
InitiateTransfer {
|
||||
destination: dest.clone(),
|
||||
remote_fees: Some(AssetTransferFilter::ReserveWithdraw(assets.clone().into())),
|
||||
preserve_origin: false,
|
||||
assets: BoundedVec::new(),
|
||||
remote_xcm: Default::default(),
|
||||
},
|
||||
]);
|
||||
assert!(matches!(
|
||||
<AssetHubZagros as AssetHubZagrosPallet>::PezkuwiXcm::execute(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(receiver.clone()),
|
||||
bx!(xcm::VersionedXcm::from(xcm.into())),
|
||||
Weight::MAX,
|
||||
),
|
||||
Err(pezsp_runtime::DispatchErrorWithPostInfo { .. }),
|
||||
));
|
||||
// teleporting it back works
|
||||
let xcm = Xcm::<()>(vec![
|
||||
WithdrawAsset(assets.clone().into()),
|
||||
SetFeesMode { jit_withdraw: true },
|
||||
InitiateTransfer {
|
||||
destination: dest,
|
||||
remote_fees: Some(AssetTransferFilter::Teleport(assets.into())),
|
||||
preserve_origin: false,
|
||||
assets: BoundedVec::new(),
|
||||
remote_xcm: xcm_on_dest,
|
||||
},
|
||||
]);
|
||||
<AssetHubZagros as AssetHubZagrosPallet>::PezkuwiXcm::execute(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(receiver.clone()),
|
||||
bx!(xcm::VersionedXcm::from(xcm.into())),
|
||||
Weight::MAX,
|
||||
)
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
let ah_sender_balance_after =
|
||||
foreign_balance_on!(AssetHubZagros, foreign_asset_location_on_ah, &receiver);
|
||||
let penpal_receiver_balance_after = assets_balance_on!(PenpalA, new_asset_id, &sender);
|
||||
|
||||
assert!(ah_sender_balance_after < ah_sender_balance_before);
|
||||
assert!(penpal_receiver_balance_after > penpal_receiver_balance_before);
|
||||
}
|
||||
|
||||
// ==============================================================================================
|
||||
// ==== Bidirectional Transfer - Reserve-based Foreign Asset - Penpal<->AssetHub ====
|
||||
// ==============================================================================================
|
||||
/// Transfers of foreign asset from Penpal to AssetHub and back. Foreign Asset is not registered
|
||||
/// with Asset Hub as a trusted reserve, ergo teleports are not available and reserve-transfers are
|
||||
/// to be used. Also verifies that teleporting the asset fails both ways.
|
||||
#[test]
|
||||
fn bidirectional_reserve_transfer_foreign_asset_between_penpal_and_asset_hub() {
|
||||
let sender = PenpalASender::get();
|
||||
let receiver = AssetHubZagrosReceiver::get();
|
||||
let new_asset_id = 42;
|
||||
let asset_amount_to_send = ASSET_HUB_ZAGROS_ED * 10_000;
|
||||
let (asset_location_on_penpal, foreign_asset_location_on_ah) =
|
||||
set_up_foreign_asset(sender.clone(), new_asset_id, asset_amount_to_send, false);
|
||||
|
||||
////////////////////////////////////////
|
||||
// Reserve-transfer it from Penpal to AH
|
||||
////////////////////////////////////////
|
||||
|
||||
let penpal_sender_balance_before = assets_balance_on!(PenpalA, new_asset_id, &sender);
|
||||
let ah_receiver_balance_before =
|
||||
foreign_balance_on!(AssetHubZagros, foreign_asset_location_on_ah.clone(), &receiver);
|
||||
|
||||
let dest = PenpalA::sibling_location_of(AssetHubZagros::para_id());
|
||||
let assets: Assets =
|
||||
vec![(asset_location_on_penpal.clone(), asset_amount_to_send).into()].into();
|
||||
// execute xcm from penpal to asset hub
|
||||
PenpalA::execute_with(|| {
|
||||
// xcm to be executed at dest
|
||||
let xcm_on_dest = Xcm(vec![
|
||||
// since this is the last hop, we don't need to further use any assets previously
|
||||
// reserved for fees (there are no further hops to cover delivery fees for); we
|
||||
// RefundSurplus to get back any unspent fees
|
||||
RefundSurplus,
|
||||
DepositAsset { assets: Wild(All), beneficiary: receiver.clone().into() },
|
||||
]);
|
||||
// teleporting the asset fails
|
||||
let xcm = Xcm::<()>(vec![
|
||||
WithdrawAsset(assets.clone().into()),
|
||||
SetFeesMode { jit_withdraw: true },
|
||||
InitiateTransfer {
|
||||
destination: dest.clone(),
|
||||
remote_fees: Some(AssetTransferFilter::Teleport(assets.clone().into())),
|
||||
preserve_origin: false,
|
||||
assets: BoundedVec::new(),
|
||||
remote_xcm: xcm_on_dest.clone(),
|
||||
},
|
||||
]);
|
||||
assert!(matches!(
|
||||
<PenpalA as PenpalAPallet>::PezkuwiXcm::execute(
|
||||
<PenpalA as Chain>::RuntimeOrigin::signed(sender.clone()),
|
||||
bx!(xcm::VersionedXcm::from(xcm.into())),
|
||||
Weight::MAX,
|
||||
),
|
||||
Err(pezsp_runtime::DispatchErrorWithPostInfo { .. }),
|
||||
));
|
||||
// reserve-transferring the asset works
|
||||
let xcm = Xcm::<()>(vec![
|
||||
WithdrawAsset(assets.clone().into()),
|
||||
SetFeesMode { jit_withdraw: true },
|
||||
InitiateTransfer {
|
||||
destination: dest,
|
||||
remote_fees: Some(AssetTransferFilter::ReserveDeposit(assets.into())),
|
||||
preserve_origin: false,
|
||||
assets: BoundedVec::new(),
|
||||
remote_xcm: xcm_on_dest,
|
||||
},
|
||||
]);
|
||||
assert_ok!(<PenpalA as PenpalAPallet>::PezkuwiXcm::execute(
|
||||
<PenpalA as Chain>::RuntimeOrigin::signed(sender.clone()),
|
||||
bx!(xcm::VersionedXcm::from(xcm.into())),
|
||||
Weight::MAX,
|
||||
));
|
||||
});
|
||||
|
||||
let penpal_sender_balance_after = assets_balance_on!(PenpalA, new_asset_id, &sender);
|
||||
let ah_receiver_balance_after =
|
||||
foreign_balance_on!(AssetHubZagros, foreign_asset_location_on_ah.clone(), &receiver);
|
||||
|
||||
assert!(penpal_sender_balance_after < penpal_sender_balance_before);
|
||||
assert!(ah_receiver_balance_after > ah_receiver_balance_before);
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// Reserve-transfer it back from AH to Penpal
|
||||
/////////////////////////////////////////////
|
||||
|
||||
let asset_amount_to_send = ah_receiver_balance_after;
|
||||
let ah_sender_balance_before =
|
||||
foreign_balance_on!(AssetHubZagros, foreign_asset_location_on_ah.clone(), &receiver);
|
||||
let penpal_receiver_balance_before = assets_balance_on!(PenpalA, new_asset_id, &sender);
|
||||
|
||||
let dest = AssetHubZagros::sibling_location_of(PenpalA::para_id());
|
||||
// execute xcm from asset hub to penpal
|
||||
AssetHubZagros::execute_with(|| {
|
||||
let assets: Assets =
|
||||
vec![(foreign_asset_location_on_ah.clone(), asset_amount_to_send).into()].into();
|
||||
// xcm to be executed at dest
|
||||
let xcm_on_dest = Xcm(vec![
|
||||
// since this is the last hop, we don't need to further use any assets previously
|
||||
// reserved for fees (there are no further hops to cover delivery fees for); we
|
||||
// RefundSurplus to get back any unspent fees
|
||||
RefundSurplus,
|
||||
DepositAsset { assets: Wild(All), beneficiary: sender.clone().into() },
|
||||
]);
|
||||
// teleporting the asset back to penpal fails
|
||||
let xcm = Xcm::<()>(vec![
|
||||
WithdrawAsset(assets.clone().into()),
|
||||
SetFeesMode { jit_withdraw: true },
|
||||
InitiateTransfer {
|
||||
destination: dest.clone(),
|
||||
remote_fees: Some(AssetTransferFilter::Teleport(assets.clone().into())),
|
||||
preserve_origin: false,
|
||||
assets: BoundedVec::new(),
|
||||
remote_xcm: xcm_on_dest.clone(),
|
||||
},
|
||||
]);
|
||||
assert!(matches!(
|
||||
<AssetHubZagros as AssetHubZagrosPallet>::PezkuwiXcm::execute(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(receiver.clone()),
|
||||
bx!(xcm::VersionedXcm::from(xcm.into())),
|
||||
Weight::MAX,
|
||||
),
|
||||
Err(pezsp_runtime::DispatchErrorWithPostInfo { .. }),
|
||||
));
|
||||
// but reserve-transferring it back works
|
||||
let xcm = Xcm::<()>(vec![
|
||||
WithdrawAsset(assets.clone().into()),
|
||||
SetFeesMode { jit_withdraw: true },
|
||||
InitiateTransfer {
|
||||
destination: dest,
|
||||
remote_fees: Some(AssetTransferFilter::ReserveWithdraw(assets.into())),
|
||||
preserve_origin: false,
|
||||
assets: BoundedVec::new(),
|
||||
remote_xcm: xcm_on_dest,
|
||||
},
|
||||
]);
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::PezkuwiXcm::execute(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(receiver.clone()),
|
||||
bx!(xcm::VersionedXcm::from(xcm.into())),
|
||||
Weight::MAX,
|
||||
));
|
||||
});
|
||||
|
||||
let ah_sender_balance_after =
|
||||
foreign_balance_on!(AssetHubZagros, foreign_asset_location_on_ah, &receiver);
|
||||
let penpal_receiver_balance_after = assets_balance_on!(PenpalA, new_asset_id, &sender);
|
||||
|
||||
assert!(ah_sender_balance_after < ah_sender_balance_before);
|
||||
assert!(penpal_receiver_balance_after > penpal_receiver_balance_before);
|
||||
}
|
||||
|
||||
/// Verifies that foreign asset reserves can be only set by signed `Owner` account or through XCM
|
||||
/// using remote `ManagerOrigin`.
|
||||
#[test]
|
||||
fn verify_foreign_asset_origin_checks() {
|
||||
let sender = PenpalASender::get();
|
||||
let new_asset_id = 42;
|
||||
let asset_amount_to_send = ASSET_HUB_ZAGROS_ED * 10_000;
|
||||
let (_, foreign_asset_location_on_ah) =
|
||||
set_up_foreign_asset(sender.clone(), new_asset_id, asset_amount_to_send, false);
|
||||
|
||||
let penpal_sovereign = AssetHubZagros::sovereign_account_id_of(
|
||||
AssetHubZagros::sibling_location_of(PenpalA::para_id()),
|
||||
);
|
||||
let reserves_data = ForeignAssetReserveData {
|
||||
reserve: AssetHubZagros::sibling_location_of(PenpalA::para_id()),
|
||||
teleportable: true,
|
||||
};
|
||||
// Set asset reserves using signed `owner` account.
|
||||
let origin = <AssetHubZagros as Chain>::RuntimeOrigin::signed(penpal_sovereign);
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::set_reserves(
|
||||
origin,
|
||||
foreign_asset_location_on_ah.clone(),
|
||||
vec![reserves_data.clone()],
|
||||
)
|
||||
.unwrap();
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::ForeignAssets(pezpallet_assets::Event::ReservesUpdated { asset_id, .. }) => {
|
||||
asset_id: *asset_id == foreign_asset_location_on_ah,
|
||||
},
|
||||
]
|
||||
);
|
||||
});
|
||||
// Now set asset reserves using some other signed account. It should fail.
|
||||
let origin = <AssetHubZagros as Chain>::RuntimeOrigin::signed(sender.clone());
|
||||
AssetHubZagros::execute_with(|| {
|
||||
assert!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::set_reserves(
|
||||
origin,
|
||||
foreign_asset_location_on_ah.clone(),
|
||||
vec![reserves_data],
|
||||
)
|
||||
.is_err());
|
||||
});
|
||||
// Now set asset reserves using remote XCM from correct origin chain.
|
||||
// Use wrong `{origin, asset}` combination.
|
||||
let asset_id_on_ah = emulated_integration_tests_common::PenpalBTeleportableAssetLocation::get();
|
||||
penpal_set_foreign_asset_reserves_on_asset_hub(asset_id_on_ah, vec![]);
|
||||
// Verify it failed.
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed { success: false, .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
// Verify it works when using right `{origin, asset}` combination.
|
||||
let asset_id_on_ah = foreign_asset_location_on_ah;
|
||||
penpal_set_foreign_asset_reserves_on_asset_hub(asset_id_on_ah.clone(), vec![]);
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
AssetHubZagros::assert_xcmp_queue_success(None);
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
// Foreign Asset created
|
||||
RuntimeEvent::ForeignAssets(pezpallet_assets::Event::ReservesRemoved { asset_id }) => {
|
||||
asset_id: *asset_id == asset_id_on_ah,
|
||||
},
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
+1097
File diff suppressed because it is too large
Load Diff
+133
@@ -0,0 +1,133 @@
|
||||
// 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 aliases;
|
||||
mod claim_assets;
|
||||
mod exchange_asset;
|
||||
mod fellowship_treasury;
|
||||
mod foreign_assets;
|
||||
mod hybrid_transfers;
|
||||
mod reserve_transfer;
|
||||
mod reward_pool;
|
||||
mod send;
|
||||
mod set_asset_claimer;
|
||||
mod set_xcm_versions;
|
||||
mod swap;
|
||||
mod teleport;
|
||||
mod transact;
|
||||
mod transfer_assets_validation;
|
||||
mod treasury;
|
||||
mod xcm_fee_estimation;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! foreign_balance_on {
|
||||
( $chain:ident, $id:expr, $who:expr ) => {
|
||||
emulated_integration_tests_common::impls::paste::paste! {
|
||||
<$chain>::execute_with(|| {
|
||||
type ForeignAssets = <$chain as [<$chain Pallet>]>::ForeignAssets;
|
||||
<ForeignAssets as pezframe_support::traits::fungibles::Inspect<_>>::balance($id, $who)
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! assets_balance_on {
|
||||
( $chain:ident, $id:expr, $who:expr ) => {
|
||||
emulated_integration_tests_common::impls::paste::paste! {
|
||||
<$chain>::execute_with(|| {
|
||||
type Assets = <$chain as [<$chain Pallet>]>::Assets;
|
||||
<Assets as pezframe_support::traits::fungibles::Inspect<_>>::balance($id, $who)
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! create_pool_with_wnd_on {
|
||||
// default amounts
|
||||
( $chain:ident, $asset_id:expr, $is_foreign:expr, $asset_owner:expr ) => {
|
||||
$crate::create_pool_with_wnd_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, $wnd_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 wnd_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(wnd_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(wnd_location),
|
||||
Box::new($asset_id),
|
||||
$wnd_amount,
|
||||
$asset_amount,
|
||||
0,
|
||||
0,
|
||||
owner.into()
|
||||
));
|
||||
|
||||
assert_expected_events!(
|
||||
$chain,
|
||||
vec![
|
||||
RuntimeEvent::AssetConversion(pezpallet_asset_conversion::Event::LiquidityAdded { .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
+1503
File diff suppressed because it is too large
Load Diff
+114
@@ -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 codec::Encode;
|
||||
use pezframe_support::{assert_ok, pezsp_runtime::traits::Dispatchable, traits::schedule::DispatchTime};
|
||||
use xcm_executor::traits::ConvertLocation;
|
||||
|
||||
#[test]
|
||||
fn treasury_creates_asset_reward_pool() {
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
type Balances = <AssetHubZagros as AssetHubZagrosPallet>::Balances;
|
||||
|
||||
let treasurer =
|
||||
Location::new(1, [Plurality { id: BodyId::Treasury, part: BodyPart::Voice }]);
|
||||
let treasurer_account =
|
||||
ahw_xcm_config::LocationToAccountId::convert_location(&treasurer).unwrap();
|
||||
|
||||
assert_ok!(Balances::force_set_balance(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::root(),
|
||||
treasurer_account.clone().into(),
|
||||
ASSET_HUB_ZAGROS_ED * 100_000,
|
||||
));
|
||||
|
||||
let events = AssetHubZagros::events();
|
||||
match events.iter().last() {
|
||||
Some(RuntimeEvent::Balances(pezpallet_balances::Event::BalanceSet { who, .. })) => {
|
||||
assert_eq!(*who, treasurer_account)
|
||||
},
|
||||
_ => panic!("Expected Balances::BalanceSet event"),
|
||||
}
|
||||
});
|
||||
Zagros::execute_with(|| {
|
||||
type AssetHubZagrosRuntimeCall = <AssetHubZagros as Chain>::RuntimeCall;
|
||||
type AssetHubZagrosRuntime = <AssetHubZagros as Chain>::Runtime;
|
||||
type ZagrosRuntimeCall = <Zagros as Chain>::RuntimeCall;
|
||||
type ZagrosRuntime = <Zagros as Chain>::Runtime;
|
||||
type ZagrosRuntimeEvent = <Zagros as Chain>::RuntimeEvent;
|
||||
type ZagrosRuntimeOrigin = <Zagros as Chain>::RuntimeOrigin;
|
||||
|
||||
Dmp::make_teyrchain_reachable(AssetHubZagros::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 =
|
||||
ZagrosRuntimeCall::XcmPallet(pezpallet_xcm::Call::<ZagrosRuntime>::send {
|
||||
dest: bx!(VersionedLocation::V4(
|
||||
xcm::v4::Junction::Teyrchain(AssetHubZagros::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: AssetHubZagrosRuntimeCall::AssetRewards(
|
||||
pezpallet_asset_rewards::Call::<AssetHubZagrosRuntime>::create_pool {
|
||||
staked_asset_id,
|
||||
reward_asset_id,
|
||||
reward_rate_per_block,
|
||||
expiry: DispatchTime::After(lifetime),
|
||||
admin
|
||||
}
|
||||
)
|
||||
.encode()
|
||||
.into(),
|
||||
}
|
||||
]))),
|
||||
});
|
||||
|
||||
let treasury_origin: ZagrosRuntimeOrigin = Treasurer.into();
|
||||
assert_ok!(create_pool_call.dispatch(treasury_origin));
|
||||
|
||||
assert_expected_events!(
|
||||
Zagros,
|
||||
vec![
|
||||
ZagrosRuntimeEvent::XcmPallet(pezpallet_xcm::Event::Sent { .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type Runtime = <AssetHubZagros as Chain>::Runtime;
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
|
||||
assert_eq!(1, pezpallet_asset_rewards::Pools::<Runtime>::iter().count());
|
||||
|
||||
let events = AssetHubZagros::events();
|
||||
match events.iter().last() {
|
||||
Some(RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed {
|
||||
success: true,
|
||||
..
|
||||
})) => (),
|
||||
_ => panic!("Expected MessageQueue::Processed event"),
|
||||
}
|
||||
});
|
||||
}
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
// 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_wnd_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() {
|
||||
AssetHubZagros::force_create_asset_from_relay_as_root(
|
||||
ASSET_ID,
|
||||
ASSET_MIN_BALANCE,
|
||||
true,
|
||||
AssetHubZagrosSender::get().into(),
|
||||
Some(Weight::from_parts(78_628_000, 3675)),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn penpal_register_foreign_asset_on_asset_hub(asset_location_on_penpal: Location) {
|
||||
let penpal_sovereign_account = AssetHubZagros::sovereign_account_id_of(
|
||||
AssetHubZagros::sibling_location_of(PenpalA::para_id()),
|
||||
);
|
||||
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 = AssetHubZagros::create_foreign_asset_call(
|
||||
foreign_asset_at_asset_hub.clone(),
|
||||
ASSET_MIN_BALANCE,
|
||||
penpal_sovereign_account.clone(),
|
||||
);
|
||||
|
||||
let origin_kind = OriginKind::Xcm;
|
||||
let fee_amount = ASSET_HUB_ZAGROS_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(AssetHubZagros::para_id()).into();
|
||||
let xcm = xcm_transact_paid_execution(
|
||||
call,
|
||||
origin_kind,
|
||||
system_asset,
|
||||
penpal_sovereign_account.clone(),
|
||||
);
|
||||
|
||||
// SA-of-Penpal-on-AHR needs to have balance to pay for fees and asset creation deposit
|
||||
AssetHubZagros::fund_accounts(vec![(
|
||||
penpal_sovereign_account.clone().into(),
|
||||
ASSET_HUB_ZAGROS_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();
|
||||
});
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
AssetHubZagros::assert_xcmp_queue_success(None);
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
// Burned the fee
|
||||
RuntimeEvent::Balances(pezpallet_balances::Event::Burned { who, amount }) => {
|
||||
who: *who == penpal_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 == penpal_sovereign_account.clone(),
|
||||
owner: *owner == penpal_sovereign_account,
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
type ForeignAssets = <AssetHubZagros as AssetHubZagrosPallet>::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 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 asset_location_on_penpal = Location::new(
|
||||
0,
|
||||
[Junction::PalletInstance(ASSETS_PALLET_ID), Junction::GeneralIndex(ASSET_ID.into())],
|
||||
);
|
||||
penpal_register_foreign_asset_on_asset_hub(asset_location_on_penpal);
|
||||
}
|
||||
|
||||
/// 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 = AssetHubZagros::sovereign_account_id_of(
|
||||
AssetHubZagros::sibling_location_of(PenpalA::para_id()),
|
||||
);
|
||||
|
||||
// Force create and mint sufficient assets for Teyrchain's sovereign account
|
||||
AssetHubZagros::force_create_and_mint_asset(
|
||||
ASSET_ID,
|
||||
ASSET_MIN_BALANCE,
|
||||
true,
|
||||
para_sovereign_account.clone(),
|
||||
Some(Weight::from_parts(78_628_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 = AssetHubZagros::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(AssetHubZagros::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
|
||||
AssetHubZagros::fund_accounts(vec![(
|
||||
para_sovereign_account.clone().into(),
|
||||
ASSET_HUB_ZAGROS_ED * 10000000000,
|
||||
)]);
|
||||
|
||||
create_pool_with_wnd_on!(
|
||||
AssetHubZagros,
|
||||
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();
|
||||
});
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
AssetHubZagros::assert_xcmp_queue_success(None);
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
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,
|
||||
},
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
// 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::{bhw_xcm_config::LocationToAccountId, *};
|
||||
use emulated_integration_tests_common::{
|
||||
accounts::{ALICE, BOB},
|
||||
impls::AccountId32,
|
||||
};
|
||||
use pezframe_support::{assert_ok, pezsp_runtime::traits::Dispatchable};
|
||||
use xcm_executor::traits::ConvertLocation;
|
||||
use zagros_system_emulated_network::{
|
||||
asset_hub_zagros_emulated_chain::asset_hub_zagros_runtime::RuntimeOrigin as AssetHubRuntimeOrigin,
|
||||
bridge_hub_zagros_emulated_chain::bridge_hub_zagros_runtime::RuntimeOrigin as BridgeHubRuntimeOrigin,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_set_asset_claimer_within_a_chain() {
|
||||
let (alice_account, _) = account_and_location(ALICE);
|
||||
let (bob_account, bob_location) = account_and_location(BOB);
|
||||
|
||||
let trap_amount = 16_000_000_000_000;
|
||||
let assets: Assets = (Parent, trap_amount).into();
|
||||
|
||||
let alice_balance_before =
|
||||
<AssetHubZagros as Chain>::account_data_of(alice_account.clone()).free;
|
||||
AssetHubZagros::fund_accounts(vec![(alice_account.clone(), trap_amount * 2)]);
|
||||
let alice_balance_after =
|
||||
<AssetHubZagros as Chain>::account_data_of(alice_account.clone()).free;
|
||||
assert_eq!(alice_balance_after - alice_balance_before, trap_amount * 2);
|
||||
|
||||
type RuntimeCall = <AssetHubZagros as Chain>::RuntimeCall;
|
||||
let asset_trap_xcm = Xcm::<RuntimeCall>::builder_unsafe()
|
||||
.set_hints(vec![AssetClaimer { location: bob_location.clone() }])
|
||||
.withdraw_asset(assets.clone())
|
||||
.clear_origin()
|
||||
.build();
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
assert_ok!(RuntimeCall::PezkuwiXcm(pezpallet_xcm::Call::execute {
|
||||
message: bx!(VersionedXcm::from(asset_trap_xcm)),
|
||||
max_weight: Weight::from_parts(4_000_000_000_000, 300_000),
|
||||
})
|
||||
.dispatch(AssetHubRuntimeOrigin::signed(alice_account.clone())));
|
||||
});
|
||||
|
||||
let balance_after_trap = <AssetHubZagros as Chain>::account_data_of(alice_account.clone()).free;
|
||||
assert_eq!(alice_balance_after - balance_after_trap, trap_amount);
|
||||
|
||||
let bob_balance_before = <AssetHubZagros as Chain>::account_data_of(bob_account.clone()).free;
|
||||
let claim_xcm = Xcm::<RuntimeCall>::builder_unsafe()
|
||||
.claim_asset(assets.clone(), Here)
|
||||
.deposit_asset(AllCounted(assets.len() as u32), bob_location.clone())
|
||||
.build();
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
assert_ok!(RuntimeCall::PezkuwiXcm(pezpallet_xcm::Call::execute {
|
||||
message: bx!(VersionedXcm::from(claim_xcm)),
|
||||
max_weight: Weight::from_parts(4_000_000_000_000, 300_000),
|
||||
})
|
||||
.dispatch(AssetHubRuntimeOrigin::signed(bob_account.clone())));
|
||||
});
|
||||
|
||||
let bob_balance_after = <AssetHubZagros as Chain>::account_data_of(bob_account.clone()).free;
|
||||
assert_eq!(bob_balance_after - bob_balance_before, trap_amount);
|
||||
}
|
||||
|
||||
fn account_and_location(account: &str) -> (AccountId32, Location) {
|
||||
let account_id = AssetHubZagros::account_id_of(account);
|
||||
let account_clone = account_id.clone();
|
||||
let location: Location = [Junction::AccountId32 {
|
||||
network: Some(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
id: account_id.into(),
|
||||
}]
|
||||
.into();
|
||||
(account_clone, location)
|
||||
}
|
||||
|
||||
// The test:
|
||||
// 1. Funds Bob account on BridgeHub, withdraws the funds, sets asset claimer to
|
||||
// sibling-account-of(AssetHub/Alice) and traps the funds.
|
||||
// 2. Alice on AssetHub sends an XCM to BridgeHub to claim assets, pay fees and deposit
|
||||
// remaining to her sibling account on BridgeHub.
|
||||
#[test]
|
||||
fn test_set_asset_claimer_between_the_chains() {
|
||||
let alice = AssetHubZagros::account_id_of(ALICE);
|
||||
let alice_bh_sibling = Location::new(
|
||||
1,
|
||||
[
|
||||
Teyrchain(AssetHubZagros::para_id().into()),
|
||||
Junction::AccountId32 {
|
||||
network: Some(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
id: alice.clone().into(),
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
let bob = BridgeHubZagros::account_id_of(BOB);
|
||||
let trap_amount = 16_000_000_000_000u128;
|
||||
BridgeHubZagros::fund_accounts(vec![(bob.clone(), trap_amount * 2)]);
|
||||
|
||||
let assets: Assets = (Parent, trap_amount).into();
|
||||
type RuntimeCall = <BridgeHubZagros as Chain>::RuntimeCall;
|
||||
let trap_xcm = Xcm::<RuntimeCall>::builder_unsafe()
|
||||
.set_hints(vec![AssetClaimer { location: alice_bh_sibling.clone() }])
|
||||
.withdraw_asset(assets.clone())
|
||||
.clear_origin()
|
||||
.build();
|
||||
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
assert_ok!(RuntimeCall::PezkuwiXcm(pezpallet_xcm::Call::execute {
|
||||
message: bx!(VersionedXcm::from(trap_xcm)),
|
||||
max_weight: Weight::from_parts(4_000_000_000_000, 700_000),
|
||||
})
|
||||
.dispatch(BridgeHubRuntimeOrigin::signed(bob.clone())));
|
||||
});
|
||||
|
||||
let alice_bh_acc = LocationToAccountId::convert_location(&alice_bh_sibling).unwrap();
|
||||
let balance = <BridgeHubZagros as Chain>::account_data_of(alice_bh_acc.clone()).free;
|
||||
assert_eq!(balance, 0);
|
||||
|
||||
let pay_fees = 6_000_000_000_000u128;
|
||||
let xcm_on_bh = Xcm::<()>::builder_unsafe()
|
||||
.claim_asset(assets.clone(), Here)
|
||||
.pay_fees((Parent, pay_fees))
|
||||
.deposit_asset(All, alice_bh_sibling.clone())
|
||||
.build();
|
||||
let bh_on_ah = AssetHubZagros::sibling_location_of(BridgeHubZagros::para_id()).into();
|
||||
AssetHubZagros::execute_with(|| {
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::PezkuwiXcm::send(
|
||||
AssetHubRuntimeOrigin::signed(alice.clone()),
|
||||
bx!(bh_on_ah),
|
||||
bx!(VersionedXcm::from(xcm_on_bh)),
|
||||
));
|
||||
});
|
||||
|
||||
let alice_bh_acc = LocationToAccountId::convert_location(&alice_bh_sibling).unwrap();
|
||||
let balance = <BridgeHubZagros as Chain>::account_data_of(alice_bh_acc).free;
|
||||
assert_eq!(balance, trap_amount - pay_fees);
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
// 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 = <Zagros as Chain>::RuntimeOrigin::root();
|
||||
let system_para_destination: Location = Zagros::child_location_of(AssetHubZagros::para_id());
|
||||
|
||||
// Relay Chain sets supported version for Asset Teyrchain
|
||||
Zagros::execute_with(|| {
|
||||
assert_ok!(<Zagros as ZagrosPallet>::XcmPallet::force_xcm_version(
|
||||
sudo_origin,
|
||||
bx!(system_para_destination.clone()),
|
||||
XCM_V3
|
||||
));
|
||||
|
||||
type RuntimeEvent = <Zagros as Chain>::RuntimeEvent;
|
||||
|
||||
assert_expected_events!(
|
||||
Zagros,
|
||||
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 = AssetHubZagros::parent_location();
|
||||
let force_xcm_version_call =
|
||||
<AssetHubZagros as Chain>::RuntimeCall::PezkuwiXcm(pezpallet_xcm::Call::<
|
||||
<AssetHubZagros 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
|
||||
Zagros::send_unpaid_transact_to_teyrchain_as_root(
|
||||
AssetHubZagros::para_id(),
|
||||
force_xcm_version_call,
|
||||
);
|
||||
|
||||
// System Teyrchain receive the XCM message
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
|
||||
AssetHubZagros::assert_dmp_queue_complete(Some(Weight::from_parts(47_887_000, 0)));
|
||||
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::PezkuwiXcm(pezpallet_xcm::Event::SupportedVersionChanged {
|
||||
location,
|
||||
version: XCM_V3
|
||||
}) => { location: *location == parent_location, },
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
+400
@@ -0,0 +1,400 @@
|
||||
// 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()).expect("conversion works"));
|
||||
let asset_one = Box::new(Location {
|
||||
parents: 0,
|
||||
interior: [
|
||||
Junction::PalletInstance(ASSETS_PALLET_ID),
|
||||
Junction::GeneralIndex(ASSET_ID.into()),
|
||||
]
|
||||
.into(),
|
||||
});
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::Assets::create(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(AssetHubZagrosSender::get()),
|
||||
ASSET_ID.into(),
|
||||
AssetHubZagrosSender::get().into(),
|
||||
1000,
|
||||
));
|
||||
assert!(<AssetHubZagros as AssetHubZagrosPallet>::Assets::asset_exists(ASSET_ID));
|
||||
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::Assets::mint(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(AssetHubZagrosSender::get()),
|
||||
ASSET_ID.into(),
|
||||
AssetHubZagrosSender::get().into(),
|
||||
3_000_000_000_000,
|
||||
));
|
||||
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::AssetConversion::create_pool(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(AssetHubZagrosSender::get()),
|
||||
asset_native.clone(),
|
||||
asset_one.clone(),
|
||||
));
|
||||
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::AssetConversion(pezpallet_asset_conversion::Event::PoolCreated { .. }) => {},
|
||||
]
|
||||
);
|
||||
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::AssetConversion::add_liquidity(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(AssetHubZagrosSender::get()),
|
||||
asset_native.clone(),
|
||||
asset_one.clone(),
|
||||
1_000_000_000_000,
|
||||
2_000_000_000_000,
|
||||
0,
|
||||
0,
|
||||
AssetHubZagrosSender::get().into()
|
||||
));
|
||||
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
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!(
|
||||
<AssetHubZagros as AssetHubZagrosPallet>::AssetConversion::swap_exact_tokens_for_tokens(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(AssetHubZagrosSender::get()),
|
||||
path,
|
||||
100,
|
||||
1,
|
||||
AssetHubZagrosSender::get().into(),
|
||||
true
|
||||
)
|
||||
);
|
||||
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::AssetConversion(pezpallet_asset_conversion::Event::SwapExecuted { amount_in, amount_out, .. }) => {
|
||||
amount_in: *amount_in == 100,
|
||||
amount_out: *amount_out == 199,
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::AssetConversion::remove_liquidity(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(AssetHubZagrosSender::get()),
|
||||
asset_native.clone(),
|
||||
asset_one.clone(),
|
||||
1414213562273 - 2_000_000_000, // all but the 2 EDs can't be retrieved.
|
||||
0,
|
||||
0,
|
||||
AssetHubZagrosSender::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_zagros =
|
||||
Location::new(1, [Junction::Teyrchain(PenpalA::para_id().into())])
|
||||
.appended_with(asset_location_on_penpal)
|
||||
.unwrap();
|
||||
|
||||
let penpal_as_seen_by_ah = AssetHubZagros::sibling_location_of(PenpalA::para_id());
|
||||
let sov_penpal_on_ahr = AssetHubZagros::sovereign_account_id_of(penpal_as_seen_by_ah);
|
||||
AssetHubZagros::fund_accounts(vec![
|
||||
// An account to swap dot for something else.
|
||||
(AssetHubZagrosSender::get().into(), 5_000_000 * ASSET_HUB_ZAGROS_ED),
|
||||
// Penpal's sovereign account in AH should have some balance
|
||||
(sov_penpal_on_ahr.clone().into(), 100_000_000 * ASSET_HUB_ZAGROS_ED),
|
||||
]);
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
// 0: No need to create foreign asset as it exists in genesis.
|
||||
//
|
||||
// 1: Mint foreign asset on asset_hub_zagros:
|
||||
//
|
||||
// (While it might be nice to use batch,
|
||||
// currently that's disabled due to safe call filters.)
|
||||
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
// 1. Mint foreign asset (in reality this should be a teleport or some such)
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::mint(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(sov_penpal_on_ahr.clone().into()),
|
||||
foreign_asset_at_asset_hub_zagros.clone(),
|
||||
sov_penpal_on_ahr.clone().into(),
|
||||
ASSET_HUB_ZAGROS_ED * 3_000_000_000_000,
|
||||
));
|
||||
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::ForeignAssets(pezpallet_assets::Event::Issued { .. }) => {},
|
||||
]
|
||||
);
|
||||
|
||||
// 2. Create pool:
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::AssetConversion::create_pool(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(AssetHubZagrosSender::get()),
|
||||
asset_native.clone(),
|
||||
Box::new(foreign_asset_at_asset_hub_zagros.clone()),
|
||||
));
|
||||
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::AssetConversion(pezpallet_asset_conversion::Event::PoolCreated { .. }) => {},
|
||||
]
|
||||
);
|
||||
|
||||
// 3. Add liquidity:
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::AssetConversion::add_liquidity(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(sov_penpal_on_ahr.clone()),
|
||||
asset_native.clone(),
|
||||
Box::new(foreign_asset_at_asset_hub_zagros.clone()),
|
||||
1_000_000_000_000_000,
|
||||
2_000_000_000_000_000,
|
||||
0,
|
||||
0,
|
||||
sov_penpal_on_ahr.clone().into()
|
||||
));
|
||||
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::AssetConversion(pezpallet_asset_conversion::Event::LiquidityAdded {lp_token_minted, .. }) => {
|
||||
lp_token_minted: *lp_token_minted == 1414213562372995,
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
// 4. Swap!
|
||||
let path = vec![asset_native.clone(), Box::new(foreign_asset_at_asset_hub_zagros.clone())];
|
||||
|
||||
assert_ok!(
|
||||
<AssetHubZagros as AssetHubZagrosPallet>::AssetConversion::swap_exact_tokens_for_tokens(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(AssetHubZagrosSender::get()),
|
||||
path,
|
||||
100000 * ASSET_HUB_ZAGROS_ED,
|
||||
1000 * ASSET_HUB_ZAGROS_ED,
|
||||
AssetHubZagrosSender::get().into(),
|
||||
true
|
||||
)
|
||||
);
|
||||
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::AssetConversion(pezpallet_asset_conversion::Event::SwapExecuted { amount_in, amount_out, .. },) => {
|
||||
amount_in: *amount_in == 100000000000000,
|
||||
amount_out: *amount_out == 181322178776029,
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
// 5. Remove liquidity
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::AssetConversion::remove_liquidity(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(sov_penpal_on_ahr.clone()),
|
||||
asset_native.clone(),
|
||||
Box::new(foreign_asset_at_asset_hub_zagros),
|
||||
1414213562372995 - ASSET_HUB_ZAGROS_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 = ahw_xcm_config::PoolAssetsPalletLocation::get();
|
||||
asset_one.append_with(GeneralIndex(ASSET_ID.into())).expect("pool assets");
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
let pool_owner_account_id = AssetHubZagrosAssetConversionOrigin::get();
|
||||
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::PoolAssets::create(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(pool_owner_account_id.clone()),
|
||||
ASSET_ID.into(),
|
||||
pool_owner_account_id.clone().into(),
|
||||
1000,
|
||||
));
|
||||
assert!(<AssetHubZagros as AssetHubZagrosPallet>::PoolAssets::asset_exists(ASSET_ID));
|
||||
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::PoolAssets::mint(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(pool_owner_account_id),
|
||||
ASSET_ID.into(),
|
||||
AssetHubZagrosSender::get().into(),
|
||||
3_000_000_000_000,
|
||||
));
|
||||
|
||||
assert_matches::assert_matches!(
|
||||
<AssetHubZagros as AssetHubZagrosPallet>::AssetConversion::create_pool(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(AssetHubZagrosSender::get()),
|
||||
Box::new(Location::try_from(asset_native).expect("conversion works")),
|
||||
Box::new(Location::try_from(asset_one).expect("conversion works")),
|
||||
),
|
||||
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()).expect("conversion works");
|
||||
let asset_one = Location {
|
||||
parents: 0,
|
||||
interior: [
|
||||
Junction::PalletInstance(ASSETS_PALLET_ID),
|
||||
Junction::GeneralIndex(ASSET_ID.into()),
|
||||
]
|
||||
.into(),
|
||||
};
|
||||
let penpal = AssetHubZagros::sovereign_account_id_of(AssetHubZagros::sibling_location_of(
|
||||
PenpalA::para_id(),
|
||||
));
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
|
||||
// set up pool with ASSET_ID <> NATIVE pair
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::Assets::create(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(AssetHubZagrosSender::get()),
|
||||
ASSET_ID.into(),
|
||||
AssetHubZagrosSender::get().into(),
|
||||
ASSET_MIN_BALANCE,
|
||||
));
|
||||
assert!(<AssetHubZagros as AssetHubZagrosPallet>::Assets::asset_exists(ASSET_ID));
|
||||
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::Assets::mint(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(AssetHubZagrosSender::get()),
|
||||
ASSET_ID.into(),
|
||||
AssetHubZagrosSender::get().into(),
|
||||
3_000_000_000_000,
|
||||
));
|
||||
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::AssetConversion::create_pool(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(AssetHubZagrosSender::get()),
|
||||
Box::new(asset_native.clone()),
|
||||
Box::new(asset_one.clone()),
|
||||
));
|
||||
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::AssetConversion(pezpallet_asset_conversion::Event::PoolCreated { .. }) => {},
|
||||
]
|
||||
);
|
||||
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::AssetConversion::add_liquidity(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(AssetHubZagrosSender::get()),
|
||||
Box::new(asset_native),
|
||||
Box::new(asset_one),
|
||||
1_000_000_000_000,
|
||||
2_000_000_000_000,
|
||||
0,
|
||||
0,
|
||||
AssetHubZagrosSender::get().into()
|
||||
));
|
||||
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
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!(
|
||||
<AssetHubZagros as AssetHubZagrosPallet>::Balances::free_balance(penpal.clone()),
|
||||
0
|
||||
);
|
||||
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::Assets::touch_other(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(AssetHubZagrosSender::get()),
|
||||
ASSET_ID.into(),
|
||||
penpal.clone().into(),
|
||||
));
|
||||
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::Assets::mint(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(AssetHubZagrosSender::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
|
||||
// `AssetHubZagros`
|
||||
let call = <AssetHubZagros as Chain>::RuntimeCall::System(pezframe_system::Call::<
|
||||
<AssetHubZagros 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(AssetHubZagros::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();
|
||||
});
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
|
||||
AssetHubZagros::assert_xcmp_queue_success(None);
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
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!(AssetHubZagros);
|
||||
}
|
||||
+793
@@ -0,0 +1,793 @@
|
||||
// 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::{foreign_balance_on, imports::*};
|
||||
|
||||
fn relay_origin_assertions(t: RelayToSystemParaTest) {
|
||||
type RuntimeEvent = <Zagros as Chain>::RuntimeEvent;
|
||||
Zagros::assert_xcm_pallet_attempted_complete(None);
|
||||
assert_expected_events!(
|
||||
Zagros,
|
||||
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,
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
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 = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
let sov_penpal_on_ahr = AssetHubZagros::sovereign_account_id_of(
|
||||
AssetHubZagros::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();
|
||||
|
||||
AssetHubZagros::assert_xcmp_queue_success(None);
|
||||
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
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 / 2,
|
||||
},
|
||||
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 = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
AssetHubZagros::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();
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
// 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 relay_to_system_para_limited_teleport_assets(t: RelayToSystemParaTest) -> DispatchResult {
|
||||
Dmp::make_teyrchain_reachable(AssetHubZagros::para_id());
|
||||
|
||||
<Zagros as ZagrosPallet>::XcmPallet::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 {
|
||||
<AssetHubZagros as AssetHubZagrosPallet>::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_ZAGROS_ED * 100;
|
||||
let native_asset: Assets = (Parent, amount).into();
|
||||
let fee_asset_id: AssetId = Parent.into();
|
||||
|
||||
test_teyrchain_is_trusted_teleporter!(
|
||||
AssetHubZagros, // Origin
|
||||
vec![BridgeHubZagros], // 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_ZAGROS_ED * 100;
|
||||
let native_asset: Assets = (Parent, amount).into();
|
||||
let fee_asset_id: AssetId = Parent.into();
|
||||
|
||||
test_teyrchain_is_trusted_teleporter!(
|
||||
AssetHubZagros, // Origin
|
||||
vec![BridgeHubZagros], // Destinations
|
||||
(native_asset, amount),
|
||||
fee_asset_id,
|
||||
transfer_assets
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleport_via_limited_teleport_assets_from_and_to_relay() {
|
||||
let amount = ZAGROS_ED * 100;
|
||||
|
||||
test_relay_is_trusted_teleporter!(
|
||||
Zagros,
|
||||
vec![AssetHubZagros],
|
||||
amount,
|
||||
limited_teleport_assets
|
||||
);
|
||||
|
||||
test_teyrchain_is_trusted_teleporter_for_relay!(
|
||||
AssetHubZagros,
|
||||
Zagros,
|
||||
amount,
|
||||
limited_teleport_assets
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleport_via_transfer_assets_from_and_to_relay() {
|
||||
let amount = ZAGROS_ED * 100;
|
||||
|
||||
test_relay_is_trusted_teleporter!(Zagros, vec![AssetHubZagros], amount, transfer_assets);
|
||||
|
||||
test_teyrchain_is_trusted_teleporter_for_relay!(
|
||||
AssetHubZagros,
|
||||
Zagros,
|
||||
amount,
|
||||
transfer_assets
|
||||
);
|
||||
}
|
||||
|
||||
/// Limited Teleport of native asset from Relay Chain to Asset Hub
|
||||
/// shouldn't work when there is not enough balance in Asset Hub's `CheckAccount`
|
||||
#[test]
|
||||
fn limited_teleport_native_assets_from_relay_to_asset_hub_checking_acc_fails() {
|
||||
let check_account = AssetHubZagros::execute_with(|| {
|
||||
<AssetHubZagros as AssetHubZagrosPallet>::PezkuwiXcm::check_account()
|
||||
});
|
||||
let amount_to_send_larger_than_checking_acc: Balance =
|
||||
AssetHubZagros::account_data_of(check_account).free + 1;
|
||||
let destination = Zagros::child_location_of(AssetHubZagros::para_id());
|
||||
let beneficiary_id = AssetHubZagrosReceiver::get().into();
|
||||
let assets = (Here, amount_to_send_larger_than_checking_acc).into();
|
||||
let fee_asset_id: AssetId = Here.into();
|
||||
|
||||
let test_args = TestContext {
|
||||
sender: ZagrosSender::get(),
|
||||
receiver: AssetHubZagrosReceiver::get(),
|
||||
args: TestArgs::new_para(
|
||||
destination,
|
||||
beneficiary_id,
|
||||
amount_to_send_larger_than_checking_acc,
|
||||
assets,
|
||||
None,
|
||||
fee_asset_id,
|
||||
),
|
||||
};
|
||||
|
||||
let mut test = RelayToSystemParaTest::new(test_args);
|
||||
|
||||
let sender_balance_before = test.sender.balance;
|
||||
let receiver_balance_before = test.receiver.balance;
|
||||
|
||||
fn para_dest_assertions_fails(_t: RelayToSystemParaTest) {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: false, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
test.set_assertion::<AssetHubZagros>(para_dest_assertions_fails);
|
||||
test.set_assertion::<Zagros>(relay_origin_assertions);
|
||||
test.set_dispatchable::<Zagros>(relay_to_system_para_limited_teleport_assets);
|
||||
test.assert();
|
||||
|
||||
let sender_balance_after = test.sender.balance;
|
||||
let receiver_balance_after = test.receiver.balance;
|
||||
|
||||
let delivery_fees = Zagros::execute_with(|| {
|
||||
xcm_helpers::teleport_assets_delivery_fees::<
|
||||
<ZagrosXcmConfig as xcm_executor::Config>::XcmSender,
|
||||
>(
|
||||
test.args.assets.clone(),
|
||||
test.args.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_larger_than_checking_acc - delivery_fees,
|
||||
sender_balance_after
|
||||
);
|
||||
// Receiver's balance does not change
|
||||
assert_eq!(receiver_balance_after, receiver_balance_before);
|
||||
}
|
||||
|
||||
/// Checking account should correctly account for incoming teleports.
|
||||
#[test]
|
||||
fn limited_teleport_native_assets_from_relay_to_asset_hub_checking_acc_burn_works() {
|
||||
// Init values for Relay Chain
|
||||
let amount_to_send: Balance = ASSET_HUB_ZAGROS_ED * 1000;
|
||||
let destination = Zagros::child_location_of(AssetHubZagros::para_id());
|
||||
let beneficiary_id = AssetHubZagrosReceiver::get().into();
|
||||
let assets = (Here, amount_to_send).into();
|
||||
let fee_asset_id: AssetId = Here.into();
|
||||
|
||||
let test_args = TestContext {
|
||||
sender: ZagrosSender::get(),
|
||||
receiver: AssetHubZagrosReceiver::get(),
|
||||
args: TestArgs::new_para(
|
||||
destination,
|
||||
beneficiary_id,
|
||||
amount_to_send,
|
||||
assets,
|
||||
None,
|
||||
fee_asset_id,
|
||||
),
|
||||
};
|
||||
|
||||
let mut test = RelayToSystemParaTest::new(test_args);
|
||||
|
||||
let sender_balance_before = test.sender.balance;
|
||||
let receiver_balance_before = test.receiver.balance;
|
||||
|
||||
fn para_dest_assertions_works(t: RelayToSystemParaTest) {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
// Amount to teleport is burned from Asset Hub's `CheckAccount`
|
||||
RuntimeEvent::Balances(pezpallet_balances::Event::Burned { who, amount }) => {
|
||||
who: *who == <AssetHubZagros as AssetHubZagrosPallet>::PezkuwiXcm::check_account(),
|
||||
amount: *amount == t.args.amount,
|
||||
},
|
||||
RuntimeEvent::Balances(pezpallet_balances::Event::Minted { who, .. }) => {
|
||||
who: *who == t.receiver.account_id,
|
||||
},
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
test.set_assertion::<AssetHubZagros>(para_dest_assertions_works);
|
||||
test.set_assertion::<Zagros>(relay_origin_assertions);
|
||||
test.set_dispatchable::<Zagros>(relay_to_system_para_limited_teleport_assets);
|
||||
test.assert();
|
||||
|
||||
let sender_balance_after = test.sender.balance;
|
||||
let receiver_balance_after = test.receiver.balance;
|
||||
|
||||
let delivery_fees = Zagros::execute_with(|| {
|
||||
xcm_helpers::teleport_assets_delivery_fees::<
|
||||
<ZagrosXcmConfig as xcm_executor::Config>::XcmSender,
|
||||
>(
|
||||
test.args.assets.clone(),
|
||||
test.args.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 asset balance is increased
|
||||
assert!(receiver_balance_after > receiver_balance_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_balance_after < receiver_balance_before + amount_to_send);
|
||||
}
|
||||
|
||||
/// Checking account should correctly account for outgoing teleports.
|
||||
#[test]
|
||||
fn limited_teleport_native_assets_from_asset_hub_to_relay_checking_acc_mint_works() {
|
||||
// Init values for Relay Chain
|
||||
let amount_to_send: Balance = ASSET_HUB_ZAGROS_ED * 1000;
|
||||
let destination = AssetHubZagros::parent_location().into();
|
||||
let beneficiary_id = ZagrosReceiver::get().into();
|
||||
let assets = (Parent, amount_to_send).into();
|
||||
let fee_asset_id: AssetId = Parent.into();
|
||||
|
||||
let test_args = TestContext {
|
||||
sender: AssetHubZagrosSender::get(),
|
||||
receiver: ZagrosReceiver::get(),
|
||||
args: TestArgs::new_para(
|
||||
destination,
|
||||
beneficiary_id,
|
||||
amount_to_send,
|
||||
assets,
|
||||
None,
|
||||
fee_asset_id,
|
||||
),
|
||||
};
|
||||
|
||||
let mut test = SystemParaToRelayTest::new(test_args);
|
||||
|
||||
let sender_balance_before = test.sender.balance;
|
||||
let receiver_balance_before = test.receiver.balance;
|
||||
|
||||
fn para_origin_assertions(t: SystemParaToRelayTest) {
|
||||
AssetHubZagros::assert_xcm_pallet_attempted_complete(None);
|
||||
|
||||
AssetHubZagros::assert_teyrchain_system_ump_sent();
|
||||
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::Balances(
|
||||
pezpallet_balances::Event::Burned { who, amount }
|
||||
) => {
|
||||
who: *who == t.sender.account_id,
|
||||
amount: *amount == t.args.amount,
|
||||
},
|
||||
// Amount to teleport is burned from Asset Hub's `CheckAccount`
|
||||
RuntimeEvent::Balances(pezpallet_balances::Event::Minted { who, amount }) => {
|
||||
who: *who == <AssetHubZagros as AssetHubZagrosPallet>::PezkuwiXcm::check_account(),
|
||||
amount: *amount == t.args.amount,
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
fn relay_dest_assertions(t: SystemParaToRelayTest) {
|
||||
type RuntimeEvent = <Zagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
Zagros,
|
||||
vec![
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
RuntimeEvent::Balances(pezpallet_balances::Event::Minted { who, .. }) => {
|
||||
who: *who == t.receiver.account_id,
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
fn system_para_limited_teleport_assets(t: SystemParaToRelayTest) -> DispatchResult {
|
||||
<AssetHubZagros as AssetHubZagrosPallet>::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,
|
||||
)
|
||||
}
|
||||
|
||||
test.set_assertion::<AssetHubZagros>(para_origin_assertions);
|
||||
test.set_assertion::<Zagros>(relay_dest_assertions);
|
||||
test.set_dispatchable::<AssetHubZagros>(system_para_limited_teleport_assets);
|
||||
test.assert();
|
||||
|
||||
let sender_balance_after = test.sender.balance;
|
||||
let receiver_balance_after = test.receiver.balance;
|
||||
|
||||
let delivery_fees = AssetHubZagros::execute_with(|| {
|
||||
xcm_helpers::teleport_assets_delivery_fees::<
|
||||
<AssetHubZagrosXcmConfig as xcm_executor::Config>::XcmSender,
|
||||
>(
|
||||
test.args.assets.clone(),
|
||||
test.args.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 asset balance is increased
|
||||
assert!(receiver_balance_after > receiver_balance_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_balance_after < receiver_balance_before + amount_to_send);
|
||||
}
|
||||
|
||||
/// 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_ZAGROS_ED * 1000;
|
||||
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_ZAGROS_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(AssetHubZagros::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 * 2,
|
||||
);
|
||||
// fund Teyrchain's check account to be able to teleport
|
||||
PenpalA::fund_accounts(vec![(penpal_check_account.clone().into(), ASSET_HUB_ZAGROS_ED * 1000)]);
|
||||
|
||||
// prefund SA of Penpal on AssetHub with enough native tokens to pay for fees
|
||||
let penpal_as_seen_by_ah = AssetHubZagros::sibling_location_of(PenpalA::para_id());
|
||||
let sov_penpal_on_ah = AssetHubZagros::sovereign_account_id_of(penpal_as_seen_by_ah);
|
||||
AssetHubZagros::fund_accounts(vec![(
|
||||
sov_penpal_on_ah.clone().into(),
|
||||
ASSET_HUB_ZAGROS_ED * 100_000_000_000,
|
||||
)]);
|
||||
|
||||
// Init values for System Teyrchain
|
||||
let foreign_asset_at_asset_hub =
|
||||
Location::new(1, [Junction::Teyrchain(PenpalA::para_id().into())])
|
||||
.appended_with(asset_location_on_penpal)
|
||||
.unwrap();
|
||||
let penpal_to_ah_beneficiary_id = AssetHubZagrosReceiver::get();
|
||||
|
||||
// Penpal to AH test args
|
||||
let penpal_to_ah_test_args = TestContext {
|
||||
sender: PenpalASender::get(),
|
||||
receiver: AssetHubZagrosReceiver::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 = foreign_balance_on!(
|
||||
PenpalA,
|
||||
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 = foreign_balance_on!(
|
||||
AssetHubZagros,
|
||||
foreign_asset_at_asset_hub.clone(),
|
||||
&AssetHubZagrosReceiver::get()
|
||||
);
|
||||
|
||||
penpal_to_ah.set_assertion::<PenpalA>(penpal_to_ah_foreign_assets_sender_assertions);
|
||||
penpal_to_ah.set_assertion::<AssetHubZagros>(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 = foreign_balance_on!(
|
||||
PenpalA,
|
||||
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 = foreign_balance_on!(
|
||||
AssetHubZagros,
|
||||
foreign_asset_at_asset_hub.clone(),
|
||||
&AssetHubZagrosReceiver::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
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type ForeignAssets = <AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets;
|
||||
assert_ok!(ForeignAssets::transfer(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(AssetHubZagrosReceiver::get()),
|
||||
foreign_asset_at_asset_hub.clone().try_into().unwrap(),
|
||||
AssetHubZagrosSender::get().into(),
|
||||
asset_amount_to_send,
|
||||
));
|
||||
});
|
||||
|
||||
// Only send back half the amount.
|
||||
let asset_amount_to_send = asset_amount_to_send / 2;
|
||||
let fee_amount_to_send = fee_amount_to_send / 2;
|
||||
|
||||
let ah_to_penpal_beneficiary_id = PenpalAReceiver::get();
|
||||
let penpal_as_seen_by_ah = AssetHubZagros::sibling_location_of(PenpalA::para_id());
|
||||
let ah_assets: Assets = vec![
|
||||
(Parent, fee_amount_to_send).into(),
|
||||
(foreign_asset_at_asset_hub.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: AssetHubZagrosSender::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 = foreign_balance_on!(
|
||||
PenpalA,
|
||||
system_para_native_asset_location.clone(),
|
||||
&PenpalAReceiver::get()
|
||||
);
|
||||
|
||||
let ah_sender_assets_before = foreign_balance_on!(
|
||||
AssetHubZagros,
|
||||
foreign_asset_at_asset_hub.clone(),
|
||||
&AssetHubZagrosSender::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::<AssetHubZagros>(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::<AssetHubZagros>(ah_to_para_dispatchable);
|
||||
ah_to_penpal.assert();
|
||||
|
||||
let ah_sender_balance_after = ah_to_penpal.sender.balance;
|
||||
let penpal_receiver_balance_after =
|
||||
foreign_balance_on!(PenpalA, system_para_native_asset_location, &PenpalAReceiver::get());
|
||||
|
||||
let ah_sender_assets_after = foreign_balance_on!(
|
||||
AssetHubZagros,
|
||||
foreign_asset_at_asset_hub.clone(),
|
||||
&AssetHubZagrosSender::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 = AssetHubZagros::sibling_location_of(PenpalA::para_id());
|
||||
let signed_origin =
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(AssetHubZagrosSender::get().into());
|
||||
let roc_to_send: Balance = ZAGROS_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
|
||||
AssetHubZagros::execute_with(|| {
|
||||
let result = <AssetHubZagros as AssetHubZagrosPallet>::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
|
||||
AssetHubZagros::execute_with(|| {
|
||||
let xcm: Xcm<asset_hub_zagros_runtime::RuntimeCall> = Xcm(vec![
|
||||
WithdrawAsset(assets.into()),
|
||||
InitiateTeleport { assets: Wild(All), dest: destination, xcm: Xcm::<()>::new() },
|
||||
]);
|
||||
let result = <AssetHubZagros as AssetHubZagrosPallet>::PezkuwiXcm::execute(
|
||||
signed_origin,
|
||||
bx!(xcm::VersionedXcm::from(xcm)),
|
||||
Weight::MAX,
|
||||
);
|
||||
assert!(result.is_err());
|
||||
});
|
||||
}
|
||||
+669
@@ -0,0 +1,669 @@
|
||||
// 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::{assets_balance_on, create_pool_with_wnd_on, foreign_balance_on, imports::*};
|
||||
use pezframe_support::traits::tokens::fungibles::Mutate;
|
||||
use xcm_builder::{DescribeAllTerminal, DescribeFamily, HashedDescription};
|
||||
use xcm_executor::traits::ConvertLocation;
|
||||
|
||||
/// PenpalA transacts on PenpalB, paying fees using USDT. XCM has to go through Asset Hub as the
|
||||
/// reserve location of USDT. The original origin `PenpalA/PenpalASender` is proxied by Asset Hub.
|
||||
fn transfer_and_transact_in_same_xcm(
|
||||
destination: Location,
|
||||
usdt: Asset,
|
||||
beneficiary: Location,
|
||||
call: xcm::DoubleEncoded<()>,
|
||||
) {
|
||||
let signed_origin = <PenpalA as Chain>::RuntimeOrigin::signed(PenpalASender::get().into());
|
||||
let context = PenpalUniversalLocation::get();
|
||||
let asset_hub_location = PenpalA::sibling_location_of(AssetHubZagros::para_id());
|
||||
|
||||
let Fungible(total_usdt) = usdt.fun else { unreachable!() };
|
||||
|
||||
// TODO(https://github.com/pezkuwichain/pezkuwi-sdk/issues/147): dry-run to get local fees, for now use hardcoded value.
|
||||
let local_fees_amount = 80_000_000_000; // current exact value 69_200_786_622
|
||||
let ah_fees_amount = 90_000_000_000; // current exact value 79_948_099_299
|
||||
let usdt_to_ah_then_onward_amount = total_usdt - local_fees_amount - ah_fees_amount;
|
||||
|
||||
let local_fees: Asset = (usdt.id.clone(), local_fees_amount).into();
|
||||
let fees_for_ah: Asset = (usdt.id.clone(), ah_fees_amount).into();
|
||||
let usdt_to_ah_then_onward: Asset = (usdt.id.clone(), usdt_to_ah_then_onward_amount).into();
|
||||
|
||||
// xcm to be executed at dest
|
||||
let xcm_on_dest = Xcm(vec![
|
||||
Transact { origin_kind: OriginKind::Xcm, call, fallback_max_weight: None },
|
||||
ExpectTransactStatus(MaybeErrorCode::Success),
|
||||
// since this is the last hop, we don't need to further use any assets previously
|
||||
// reserved for fees (there are no further hops to cover delivery fees for); we
|
||||
// RefundSurplus to get back any unspent fees
|
||||
RefundSurplus,
|
||||
DepositAsset { assets: Wild(All), beneficiary },
|
||||
]);
|
||||
let destination = destination.reanchored(&asset_hub_location, &context).unwrap();
|
||||
let xcm_on_ah = Xcm(vec![InitiateTransfer {
|
||||
destination,
|
||||
remote_fees: Some(AssetTransferFilter::ReserveDeposit(Wild(All))),
|
||||
preserve_origin: true,
|
||||
assets: BoundedVec::new(),
|
||||
remote_xcm: xcm_on_dest,
|
||||
}]);
|
||||
let xcm = Xcm::<()>(vec![
|
||||
WithdrawAsset(usdt.into()),
|
||||
PayFees { asset: local_fees },
|
||||
InitiateTransfer {
|
||||
destination: asset_hub_location,
|
||||
remote_fees: Some(AssetTransferFilter::ReserveWithdraw(fees_for_ah.into())),
|
||||
preserve_origin: true,
|
||||
assets: BoundedVec::truncate_from(vec![AssetTransferFilter::ReserveWithdraw(
|
||||
usdt_to_ah_then_onward.into(),
|
||||
)]),
|
||||
remote_xcm: xcm_on_ah,
|
||||
},
|
||||
]);
|
||||
<PenpalA as PenpalAPallet>::PezkuwiXcm::execute(
|
||||
signed_origin,
|
||||
bx!(xcm::VersionedXcm::from(xcm.into())),
|
||||
Weight::MAX,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// PenpalA transacts on PenpalB, paying fees using USDT. XCM has to go through Asset Hub as the
|
||||
/// reserve location of USDT. The original origin `PenpalA/PenpalASender` is proxied by Asset Hub.
|
||||
#[test]
|
||||
fn transact_from_para_to_para_through_asset_hub() {
|
||||
let destination = PenpalA::sibling_location_of(PenpalB::para_id());
|
||||
let sender = PenpalASender::get();
|
||||
let fee_amount_to_send: Balance = ZAGROS_ED * 10000;
|
||||
let sender_chain_as_seen_by_asset_hub = AssetHubZagros::sibling_location_of(PenpalA::para_id());
|
||||
let sov_of_sender_on_asset_hub =
|
||||
AssetHubZagros::sovereign_account_id_of(sender_chain_as_seen_by_asset_hub);
|
||||
let receiver_as_seen_by_asset_hub = AssetHubZagros::sibling_location_of(PenpalB::para_id());
|
||||
let sov_of_receiver_on_asset_hub =
|
||||
AssetHubZagros::sovereign_account_id_of(receiver_as_seen_by_asset_hub);
|
||||
|
||||
// Create SA-of-Penpal-on-AHW with ED.
|
||||
AssetHubZagros::fund_accounts(vec![
|
||||
(sov_of_sender_on_asset_hub.clone().into(), ASSET_HUB_ZAGROS_ED),
|
||||
(sov_of_receiver_on_asset_hub.clone().into(), ASSET_HUB_ZAGROS_ED),
|
||||
]);
|
||||
|
||||
// Prefund USDT to sov account of sender.
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type Assets = <AssetHubZagros as AssetHubZagrosPallet>::Assets;
|
||||
assert_ok!(<Assets as Mutate<_>>::mint_into(
|
||||
USDT_ID,
|
||||
&sov_of_sender_on_asset_hub.clone().into(),
|
||||
fee_amount_to_send,
|
||||
));
|
||||
});
|
||||
|
||||
// We create a pool between ZGR and USDT in AssetHub.
|
||||
let usdt = Location::new(0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(USDT_ID.into())]);
|
||||
create_pool_with_wnd_on!(
|
||||
AssetHubZagros,
|
||||
usdt,
|
||||
false,
|
||||
AssetHubZagrosSender::get(),
|
||||
1_000_000_000_000,
|
||||
20_000_000_000
|
||||
);
|
||||
// We also need a pool between ZGR and USDT on PenpalA.
|
||||
create_pool_with_wnd_on!(PenpalA, PenpalUsdtFromAssetHub::get(), true, PenpalAssetOwner::get());
|
||||
// We also need a pool between ZGR and USDT on PenpalB.
|
||||
create_pool_with_wnd_on!(PenpalB, PenpalUsdtFromAssetHub::get(), true, PenpalAssetOwner::get());
|
||||
|
||||
let usdt_from_asset_hub = PenpalUsdtFromAssetHub::get();
|
||||
PenpalA::execute_with(|| {
|
||||
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
|
||||
assert_ok!(<ForeignAssets as Mutate<_>>::mint_into(
|
||||
usdt_from_asset_hub.clone(),
|
||||
&sender,
|
||||
fee_amount_to_send,
|
||||
));
|
||||
});
|
||||
|
||||
// Give the sender enough Relay tokens to pay for local delivery fees.
|
||||
PenpalA::mint_foreign_asset(
|
||||
<PenpalA as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get()),
|
||||
RelayLocation::get(),
|
||||
sender.clone(),
|
||||
10_000_000_000_000, // Large estimate to make sure it works.
|
||||
);
|
||||
|
||||
// Init values for Teyrchain Destination
|
||||
let receiver = PenpalBReceiver::get();
|
||||
|
||||
// Query initial balances
|
||||
let sender_assets_before = foreign_balance_on!(PenpalA, usdt_from_asset_hub.clone(), &sender);
|
||||
let receiver_assets_before =
|
||||
foreign_balance_on!(PenpalB, usdt_from_asset_hub.clone(), &receiver);
|
||||
|
||||
// Now register a new asset on PenpalB from PenpalA/sender account while paying fees using USDT
|
||||
// (going through Asset Hub)
|
||||
|
||||
let usdt_to_send: Asset = (usdt_from_asset_hub.clone(), fee_amount_to_send).into();
|
||||
let asset_location_on_penpal_a =
|
||||
Location::new(0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(ASSET_ID.into())]);
|
||||
let penpal_a_as_seen_by_penpal_b = PenpalB::sibling_location_of(PenpalA::para_id());
|
||||
let sender_as_seen_by_penpal_b =
|
||||
penpal_a_as_seen_by_penpal_b.clone().appended_with(sender.clone()).unwrap();
|
||||
let foreign_asset_at_penpal_b =
|
||||
penpal_a_as_seen_by_penpal_b.appended_with(asset_location_on_penpal_a).unwrap();
|
||||
// Encoded `create_asset` call to be executed in PenpalB
|
||||
let call = PenpalB::create_foreign_asset_call(
|
||||
foreign_asset_at_penpal_b.clone(),
|
||||
ASSET_MIN_BALANCE,
|
||||
receiver.clone(),
|
||||
);
|
||||
PenpalA::execute_with(|| {
|
||||
// initiate transaction
|
||||
transfer_and_transact_in_same_xcm(destination, usdt_to_send, receiver.clone().into(), call);
|
||||
|
||||
// verify expected events;
|
||||
PenpalA::assert_xcm_pallet_attempted_complete(None);
|
||||
});
|
||||
AssetHubZagros::execute_with(|| {
|
||||
let sov_penpal_a_on_ah = AssetHubZagros::sovereign_account_id_of(
|
||||
AssetHubZagros::sibling_location_of(PenpalA::para_id()),
|
||||
);
|
||||
asset_hub_hop_assertions(sov_penpal_a_on_ah);
|
||||
});
|
||||
PenpalB::execute_with(|| {
|
||||
let expected_creator =
|
||||
HashedDescription::<AccountId, DescribeFamily<DescribeAllTerminal>>::convert_location(
|
||||
&sender_as_seen_by_penpal_b,
|
||||
)
|
||||
.unwrap();
|
||||
penpal_b_assertions(foreign_asset_at_penpal_b, expected_creator, receiver.clone());
|
||||
});
|
||||
|
||||
// Query final balances
|
||||
let sender_assets_after = foreign_balance_on!(PenpalA, usdt_from_asset_hub.clone(), &sender);
|
||||
let receiver_assets_after = foreign_balance_on!(PenpalB, usdt_from_asset_hub, &receiver);
|
||||
|
||||
// Sender's balance is reduced by amount
|
||||
assert_eq!(sender_assets_after, sender_assets_before - fee_amount_to_send);
|
||||
// Receiver's balance is increased
|
||||
assert!(receiver_assets_after > receiver_assets_before);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transact_using_authorized_alias_from_para_to_asset_hub_and_back_to_para() {
|
||||
let sender = PenpalASender::get();
|
||||
let sov_of_penpal_on_asset_hub = AssetHubZagros::sovereign_account_id_of(
|
||||
AssetHubZagros::sibling_location_of(PenpalA::para_id()),
|
||||
);
|
||||
let wnd_from_teyrchain_pov: Location = RelayLocation::get();
|
||||
let usdt_asset_hub_pov =
|
||||
Location::new(0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(USDT_ID.into())]);
|
||||
let usdt_penpal_pov = PenpalUsdtFromAssetHub::get();
|
||||
let amount_of_wnd_to_transfer_to_ah = ZAGROS_ED * 1_000_000_000u128;
|
||||
let amount_of_usdt_we_want_from_exchange = 1_000_000_000u128;
|
||||
let max_amount_of_wnd_we_allow_for_exchange = 1_000_000_000_000u128;
|
||||
let sender_as_seen_from_ah = Location::new(
|
||||
1,
|
||||
[
|
||||
Teyrchain(2000),
|
||||
AccountId32 {
|
||||
network: Some(NetworkId::ByGenesis(PEZKUWICHAIN_GENESIS_HASH)),
|
||||
id: sender.clone().into(),
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
let mut topic_id_tracker = TopicIdTracker::new();
|
||||
|
||||
// SA-of-Penpal-on-AHW should contain ZGR amount equal at least the amount that will be
|
||||
// transferred-in to AH Since AH is the reserve for ZGR
|
||||
AssetHubZagros::fund_accounts(vec![(
|
||||
sov_of_penpal_on_asset_hub.clone().into(),
|
||||
ASSET_HUB_ZAGROS_ED + amount_of_wnd_to_transfer_to_ah,
|
||||
)]);
|
||||
// Give the sender enough ZGR
|
||||
PenpalA::mint_foreign_asset(
|
||||
<PenpalA as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get()),
|
||||
wnd_from_teyrchain_pov.clone(),
|
||||
sender.clone(),
|
||||
amount_of_wnd_to_transfer_to_ah,
|
||||
);
|
||||
|
||||
// We create a pool between ZGR and USDT in AssetHub so we can do the exchange
|
||||
create_pool_with_wnd_on!(
|
||||
AssetHubZagros,
|
||||
usdt_asset_hub_pov.clone(),
|
||||
false,
|
||||
AssetHubZagrosSender::get(),
|
||||
1_000_000_000_000,
|
||||
20_000_000_000
|
||||
);
|
||||
|
||||
// We add auhtorized alias on AH so sender from Penpal can AliasOrigin into itself on AH
|
||||
// (instead of aliasing into Sovereign Account of sender)
|
||||
AssetHubZagros::execute_with(|| {
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::PezkuwiXcm::add_authorized_alias(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(sender.clone()),
|
||||
Box::new(sender_as_seen_from_ah.into()),
|
||||
None
|
||||
));
|
||||
});
|
||||
|
||||
// Query initial balances
|
||||
let sender_usdt_on_penpal_before =
|
||||
foreign_balance_on!(PenpalA, usdt_penpal_pov.clone(), &sender);
|
||||
let sender_usdt_on_ah_before = assets_balance_on!(AssetHubZagros, USDT_ID, &sender);
|
||||
|
||||
// Encoded `swap_tokens_for_exact_tokens` call to be executed in AH
|
||||
let call = <AssetHubZagros as Chain>::RuntimeCall::AssetConversion(
|
||||
pezpallet_asset_conversion::Call::swap_tokens_for_exact_tokens {
|
||||
path: vec![
|
||||
Box::new(wnd_from_teyrchain_pov.clone()),
|
||||
Box::new(usdt_asset_hub_pov.clone()),
|
||||
],
|
||||
amount_out: amount_of_usdt_we_want_from_exchange,
|
||||
amount_in_max: max_amount_of_wnd_we_allow_for_exchange,
|
||||
send_to: sender.clone(),
|
||||
keep_alive: true,
|
||||
},
|
||||
)
|
||||
.encode()
|
||||
.into();
|
||||
|
||||
let asset_hub_location_penpal_pov = PenpalA::sibling_location_of(AssetHubZagros::para_id());
|
||||
let penpal_location_ah_pov = AssetHubZagros::sibling_location_of(PenpalA::para_id());
|
||||
|
||||
PenpalA::execute_with(|| {
|
||||
let sender_signed_origin = <PenpalA as Chain>::RuntimeOrigin::signed(sender.clone());
|
||||
|
||||
let local_fees_amount = 80_000_000_000_000u128;
|
||||
let remote_fees_amount = 90_000_000_000_000u128;
|
||||
|
||||
let penpal_local_fees: Asset = (wnd_from_teyrchain_pov.clone(), local_fees_amount).into();
|
||||
let ah_remote_fees: Asset = (wnd_from_teyrchain_pov.clone(), remote_fees_amount).into();
|
||||
let penpal_remote_fees: Asset = (wnd_from_teyrchain_pov.clone(), remote_fees_amount).into();
|
||||
let wnd_to_withdraw: Asset =
|
||||
(wnd_from_teyrchain_pov.clone(), amount_of_wnd_to_transfer_to_ah).into();
|
||||
|
||||
// xcm to be executed by penpal, sent by ah
|
||||
let xcm_back_on_penpal = Xcm(vec![
|
||||
RefundSurplus,
|
||||
DepositAsset { assets: Wild(All), beneficiary: sender.clone().into() },
|
||||
]);
|
||||
// xcm to be executed by ah, sent by penpal
|
||||
let xcm_on_ah = Xcm(vec![
|
||||
// aliasing into sender itself, as opposed to sender's sovereign account
|
||||
// its possible due to add_authorized_alias above
|
||||
AliasOrigin(Location::new(
|
||||
0,
|
||||
[AccountId32 { network: None, id: sender.clone().into() }],
|
||||
)),
|
||||
DepositAsset {
|
||||
assets: Definite(
|
||||
(wnd_from_teyrchain_pov.clone(), max_amount_of_wnd_we_allow_for_exchange)
|
||||
.into(),
|
||||
),
|
||||
beneficiary: sender.clone().into(),
|
||||
},
|
||||
Transact { origin_kind: OriginKind::SovereignAccount, call, fallback_max_weight: None },
|
||||
ExpectTransactStatus(MaybeErrorCode::Success),
|
||||
WithdrawAsset(
|
||||
(usdt_asset_hub_pov.clone(), amount_of_usdt_we_want_from_exchange).into(),
|
||||
),
|
||||
InitiateTransfer {
|
||||
destination: penpal_location_ah_pov,
|
||||
remote_fees: Some(AssetTransferFilter::ReserveDeposit(
|
||||
penpal_remote_fees.clone().into(),
|
||||
)),
|
||||
preserve_origin: false,
|
||||
assets: BoundedVec::truncate_from(vec![AssetTransferFilter::ReserveDeposit(Wild(
|
||||
All,
|
||||
))]),
|
||||
remote_xcm: xcm_back_on_penpal,
|
||||
},
|
||||
RefundSurplus,
|
||||
DepositAsset { assets: Wild(All), beneficiary: sender.clone().into() },
|
||||
]);
|
||||
// xcm to be executed locally on penpal as starting point
|
||||
let xcm = Xcm::<()>(vec![
|
||||
WithdrawAsset(wnd_to_withdraw.into()),
|
||||
PayFees { asset: penpal_local_fees },
|
||||
InitiateTransfer {
|
||||
destination: asset_hub_location_penpal_pov,
|
||||
remote_fees: Some(AssetTransferFilter::ReserveWithdraw(
|
||||
ah_remote_fees.clone().into(),
|
||||
)),
|
||||
preserve_origin: true,
|
||||
assets: BoundedVec::truncate_from(vec![AssetTransferFilter::ReserveWithdraw(
|
||||
Wild(All),
|
||||
)]),
|
||||
remote_xcm: xcm_on_ah,
|
||||
},
|
||||
RefundSurplus,
|
||||
DepositAsset { assets: Wild(All), beneficiary: sender.clone().into() },
|
||||
]);
|
||||
// initiate transaction
|
||||
<PenpalA as PenpalAPallet>::PezkuwiXcm::execute(
|
||||
sender_signed_origin,
|
||||
bx!(xcm::VersionedXcm::from(xcm.into())),
|
||||
Weight::MAX,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// verify expected events;
|
||||
PenpalA::assert_xcm_pallet_attempted_complete(None);
|
||||
|
||||
let msg_sent_id = find_xcm_sent_message_id::<PenpalA>().expect("Missing Sent Event");
|
||||
topic_id_tracker.insert("PenpalA_sent", msg_sent_id.into());
|
||||
});
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
RuntimeEvent::AssetConversion(
|
||||
pezpallet_asset_conversion::Event::SwapExecuted { amount_out, ..}
|
||||
) => { amount_out: *amount_out == amount_of_usdt_we_want_from_exchange, },
|
||||
]
|
||||
);
|
||||
|
||||
let mq_prc_id = find_mq_processed_id::<AssetHubZagros>().expect("Missing Processed Event");
|
||||
topic_id_tracker.insert("AssetHubZagros_received", mq_prc_id);
|
||||
let msg_sent_id = find_xcm_sent_message_id::<AssetHubZagros>().expect("Missing Sent Event");
|
||||
topic_id_tracker.insert("AssetHubZagros_sent", msg_sent_id.into());
|
||||
});
|
||||
|
||||
PenpalA::execute_with(|| {
|
||||
type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
PenpalA,
|
||||
vec![
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
|
||||
let mq_prc_id = find_mq_processed_id::<PenpalA>().expect("Missing Processed Event");
|
||||
topic_id_tracker.insert("PenpalA_received", mq_prc_id);
|
||||
});
|
||||
|
||||
topic_id_tracker.assert_unique();
|
||||
|
||||
// Query final balances
|
||||
let sender_usdt_on_ah_after = assets_balance_on!(AssetHubZagros, USDT_ID, &sender);
|
||||
let sender_usdt_on_penpal_after =
|
||||
foreign_balance_on!(PenpalA, usdt_penpal_pov.clone(), &sender);
|
||||
|
||||
// Receiver's balance is increased by usdt amount we got from exchange
|
||||
assert_eq!(
|
||||
sender_usdt_on_penpal_after,
|
||||
sender_usdt_on_penpal_before + amount_of_usdt_we_want_from_exchange
|
||||
);
|
||||
// Usdt amount on senders account AH side should stay the same i.e. all usdt came from exchange
|
||||
// not free balance
|
||||
assert_eq!(sender_usdt_on_ah_before, sender_usdt_on_ah_after);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transact_using_sov_account_from_para_to_asset_hub_and_back_to_para() {
|
||||
let sender = PenpalASender::get();
|
||||
let sov_of_penpal_on_asset_hub = AssetHubZagros::sovereign_account_id_of(
|
||||
AssetHubZagros::sibling_location_of(PenpalA::para_id()),
|
||||
);
|
||||
let wnd_from_teyrchain_pov: Location = RelayLocation::get();
|
||||
let usdt_asset_hub_pov =
|
||||
Location::new(0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(USDT_ID.into())]);
|
||||
let usdt_penpal_pov = PenpalUsdtFromAssetHub::get();
|
||||
let amount_of_wnd_to_transfer_to_ah = ZAGROS_ED * 1_000_000_000u128;
|
||||
let amount_of_usdt_we_want_from_exchange = 1_000_000_000u128;
|
||||
let max_amount_of_wnd_we_allow_for_exchange = 1_000_000_000_000u128;
|
||||
let sender_as_seen_from_ah = Location::new(
|
||||
1,
|
||||
[
|
||||
Teyrchain(2000),
|
||||
AccountId32 {
|
||||
network: Some(NetworkId::ByGenesis(PEZKUWICHAIN_GENESIS_HASH)),
|
||||
id: sender.clone().into(),
|
||||
},
|
||||
],
|
||||
);
|
||||
let sov_of_sender_on_asset_hub =
|
||||
AssetHubZagros::sovereign_account_id_of(sender_as_seen_from_ah.clone());
|
||||
|
||||
let mut topic_id_tracker = TopicIdTracker::new();
|
||||
|
||||
// SA-of-Penpal-on-AHW should contain ZGR amount equal at least the amount that will be
|
||||
// transferred-in to AH Since AH is the reserve for ZGR
|
||||
AssetHubZagros::fund_accounts(vec![(
|
||||
sov_of_penpal_on_asset_hub.clone().into(),
|
||||
ASSET_HUB_ZAGROS_ED + amount_of_wnd_to_transfer_to_ah,
|
||||
)]);
|
||||
// Give the sender enough ZGR
|
||||
PenpalA::mint_foreign_asset(
|
||||
<PenpalA as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get()),
|
||||
wnd_from_teyrchain_pov.clone(),
|
||||
sender.clone(),
|
||||
amount_of_wnd_to_transfer_to_ah,
|
||||
);
|
||||
|
||||
// We create a pool between ZGR and USDT in AssetHub so we can do the exchange
|
||||
create_pool_with_wnd_on!(
|
||||
AssetHubZagros,
|
||||
usdt_asset_hub_pov.clone(),
|
||||
false,
|
||||
AssetHubZagrosSender::get(),
|
||||
1_000_000_000_000,
|
||||
20_000_000_000
|
||||
);
|
||||
|
||||
// Query initial balances
|
||||
let sender_usdt_on_penpal_before =
|
||||
foreign_balance_on!(PenpalA, usdt_penpal_pov.clone(), &sender);
|
||||
let sender_usdt_on_ah_before =
|
||||
assets_balance_on!(AssetHubZagros, USDT_ID, &sov_of_sender_on_asset_hub);
|
||||
|
||||
// Encoded `swap_tokens_for_exact_tokens` call to be executed in AH
|
||||
let call = <AssetHubZagros as Chain>::RuntimeCall::AssetConversion(
|
||||
pezpallet_asset_conversion::Call::swap_tokens_for_exact_tokens {
|
||||
path: vec![
|
||||
Box::new(wnd_from_teyrchain_pov.clone()),
|
||||
Box::new(usdt_asset_hub_pov.clone()),
|
||||
],
|
||||
amount_out: amount_of_usdt_we_want_from_exchange,
|
||||
amount_in_max: max_amount_of_wnd_we_allow_for_exchange,
|
||||
send_to: sov_of_sender_on_asset_hub.clone(),
|
||||
keep_alive: false,
|
||||
},
|
||||
)
|
||||
.encode()
|
||||
.into();
|
||||
|
||||
let asset_hub_location_penpal_pov = PenpalA::sibling_location_of(AssetHubZagros::para_id());
|
||||
let penpal_location_ah_pov = AssetHubZagros::sibling_location_of(PenpalA::para_id());
|
||||
|
||||
PenpalA::execute_with(|| {
|
||||
let sender_signed_origin = <PenpalA as Chain>::RuntimeOrigin::signed(sender.clone());
|
||||
|
||||
let local_fees_amount = 80_000_000_000_000u128;
|
||||
let remote_fees_amount = 90_000_000_000_000u128;
|
||||
|
||||
let penpal_local_fees: Asset = (wnd_from_teyrchain_pov.clone(), local_fees_amount).into();
|
||||
let ah_remote_fees: Asset = (wnd_from_teyrchain_pov.clone(), remote_fees_amount).into();
|
||||
let penpal_remote_fees: Asset = (wnd_from_teyrchain_pov.clone(), remote_fees_amount).into();
|
||||
let wnd_to_withdraw: Asset =
|
||||
(wnd_from_teyrchain_pov.clone(), amount_of_wnd_to_transfer_to_ah).into();
|
||||
|
||||
// xcm to be executed by penpal, sent by ah
|
||||
let xcm_back_on_penpal = Xcm(vec![
|
||||
RefundSurplus,
|
||||
DepositAsset { assets: Wild(All), beneficiary: sender.clone().into() },
|
||||
]);
|
||||
// xcm to be executed by ah, sent by penpal
|
||||
let xcm_on_ah = Xcm(vec![
|
||||
DepositAsset {
|
||||
assets: Definite(
|
||||
(wnd_from_teyrchain_pov.clone(), max_amount_of_wnd_we_allow_for_exchange)
|
||||
.into(),
|
||||
),
|
||||
beneficiary: sov_of_sender_on_asset_hub.clone().into(),
|
||||
},
|
||||
Transact { origin_kind: OriginKind::SovereignAccount, call, fallback_max_weight: None },
|
||||
ExpectTransactStatus(MaybeErrorCode::Success),
|
||||
WithdrawAsset(
|
||||
(usdt_asset_hub_pov.clone(), amount_of_usdt_we_want_from_exchange).into(),
|
||||
),
|
||||
InitiateTransfer {
|
||||
destination: penpal_location_ah_pov,
|
||||
remote_fees: Some(AssetTransferFilter::ReserveDeposit(
|
||||
penpal_remote_fees.clone().into(),
|
||||
)),
|
||||
preserve_origin: false,
|
||||
assets: BoundedVec::truncate_from(vec![AssetTransferFilter::ReserveDeposit(Wild(
|
||||
All,
|
||||
))]),
|
||||
remote_xcm: xcm_back_on_penpal,
|
||||
},
|
||||
RefundSurplus,
|
||||
DepositAsset {
|
||||
assets: Wild(All),
|
||||
beneficiary: sov_of_sender_on_asset_hub.clone().into(),
|
||||
},
|
||||
]);
|
||||
// xcm to be executed locally on penpal as starting point
|
||||
let xcm = Xcm::<()>(vec![
|
||||
WithdrawAsset(wnd_to_withdraw.into()),
|
||||
PayFees { asset: penpal_local_fees },
|
||||
InitiateTransfer {
|
||||
destination: asset_hub_location_penpal_pov,
|
||||
remote_fees: Some(AssetTransferFilter::ReserveWithdraw(
|
||||
ah_remote_fees.clone().into(),
|
||||
)),
|
||||
preserve_origin: true,
|
||||
assets: BoundedVec::truncate_from(vec![AssetTransferFilter::ReserveWithdraw(
|
||||
Wild(All),
|
||||
)]),
|
||||
remote_xcm: xcm_on_ah,
|
||||
},
|
||||
RefundSurplus,
|
||||
DepositAsset { assets: Wild(All), beneficiary: sender.clone().into() },
|
||||
]);
|
||||
// initiate transaction
|
||||
<PenpalA as PenpalAPallet>::PezkuwiXcm::execute(
|
||||
sender_signed_origin,
|
||||
bx!(xcm::VersionedXcm::from(xcm.into())),
|
||||
Weight::MAX,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// verify expected events;
|
||||
PenpalA::assert_xcm_pallet_attempted_complete(None);
|
||||
|
||||
let msg_sent_id = find_xcm_sent_message_id::<PenpalA>().expect("Missing Sent Event");
|
||||
topic_id_tracker.insert("PenpalA_sent", msg_sent_id.into());
|
||||
});
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
RuntimeEvent::AssetConversion(
|
||||
pezpallet_asset_conversion::Event::SwapExecuted { amount_out, ..}
|
||||
) => { amount_out: *amount_out == amount_of_usdt_we_want_from_exchange, },
|
||||
]
|
||||
);
|
||||
|
||||
let mq_prc_id = find_mq_processed_id::<AssetHubZagros>().expect("Missing Processed Event");
|
||||
topic_id_tracker.insert("AssetHubZagros_received", mq_prc_id);
|
||||
let msg_sent_id = find_xcm_sent_message_id::<AssetHubZagros>().expect("Missing Sent Event");
|
||||
topic_id_tracker.insert("AssetHubZagros_sent", msg_sent_id.into());
|
||||
});
|
||||
|
||||
PenpalA::execute_with(|| {
|
||||
type RuntimeEvent = <PenpalA as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
PenpalA,
|
||||
vec![
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
|
||||
let mq_prc_id = find_mq_processed_id::<PenpalA>().expect("Missing Processed Event");
|
||||
topic_id_tracker.insert("PenpalA_received", mq_prc_id);
|
||||
});
|
||||
|
||||
topic_id_tracker.assert_unique();
|
||||
|
||||
// Query final balances
|
||||
let sender_usdt_on_ah_after =
|
||||
assets_balance_on!(AssetHubZagros, USDT_ID, &sov_of_sender_on_asset_hub);
|
||||
let sender_usdt_on_penpal_after =
|
||||
foreign_balance_on!(PenpalA, usdt_penpal_pov.clone(), &sender);
|
||||
|
||||
// Receiver's balance is increased by usdt amount we got from exchange
|
||||
assert_eq!(
|
||||
sender_usdt_on_penpal_after,
|
||||
sender_usdt_on_penpal_before + amount_of_usdt_we_want_from_exchange
|
||||
);
|
||||
// Usdt amount on senders account AH side should stay the same i.e. all usdt came from exchange
|
||||
// not free balance
|
||||
assert_eq!(sender_usdt_on_ah_before, sender_usdt_on_ah_after);
|
||||
}
|
||||
|
||||
fn asset_hub_hop_assertions(sender_sa: AccountId) {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
// Withdrawn from sender teyrchain SA
|
||||
RuntimeEvent::Assets(
|
||||
pezpallet_assets::Event::Burned { owner, .. }
|
||||
) => {
|
||||
owner: *owner == sender_sa,
|
||||
},
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
fn penpal_b_assertions(
|
||||
expected_asset: Location,
|
||||
expected_creator: AccountId,
|
||||
expected_owner: AccountId,
|
||||
) {
|
||||
type RuntimeEvent = <PenpalB as Chain>::RuntimeEvent;
|
||||
PenpalB::assert_xcmp_queue_success(None);
|
||||
assert_expected_events!(
|
||||
PenpalB,
|
||||
vec![
|
||||
RuntimeEvent::ForeignAssets(
|
||||
pezpallet_assets::Event::Created { asset_id, creator, owner }
|
||||
) => {
|
||||
asset_id: *asset_id == expected_asset,
|
||||
creator: *creator == expected_creator,
|
||||
owner: *owner == expected_owner,
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
+267
@@ -0,0 +1,267 @@
|
||||
// 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 the validation of `pezpallet_xcm::Pallet::<T>::transfer_assets`.
|
||||
//! See the `pezpallet_xcm::transfer_assets_validation` module for more information.
|
||||
|
||||
use crate::imports::*;
|
||||
use pezframe_support::{assert_err, assert_ok};
|
||||
use pezsp_runtime::DispatchError;
|
||||
|
||||
// ==================================================================================
|
||||
// ============================== PenpalA <-> Zagros ===============================
|
||||
// ==================================================================================
|
||||
|
||||
/// Test that `transfer_assets` fails when doing reserve transfer of ZGR from PenpalA to Zagros.
|
||||
/// This fails because PenpalA's IsReserve config considers Zagros as the reserve for ZGR,
|
||||
/// so transfer_assets automatically chooses reserve transfer, which we block.
|
||||
#[test]
|
||||
fn transfer_assets_wnd_reserve_transfer_para_to_relay_fails() {
|
||||
let destination = PenpalA::parent_location();
|
||||
let beneficiary: Location =
|
||||
AccountId32Junction { network: None, id: ZagrosReceiver::get().into() }.into();
|
||||
let amount_to_send: Balance = ZAGROS_ED * 1000;
|
||||
let assets: Assets = (Parent, amount_to_send).into();
|
||||
|
||||
// Mint ZGR on PenpalA for testing.
|
||||
PenpalA::mint_foreign_asset(
|
||||
<PenpalA as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get()),
|
||||
RelayLocation::get(),
|
||||
PenpalASender::get(),
|
||||
amount_to_send * 2,
|
||||
);
|
||||
|
||||
// Fund PenpalA's sovereign account on Zagros with the reserve ZGR.
|
||||
let penpal_location_as_seen_by_relay = Zagros::child_location_of(PenpalA::para_id());
|
||||
let sov_penpal_on_relay = Zagros::sovereign_account_id_of(penpal_location_as_seen_by_relay);
|
||||
Zagros::fund_accounts(vec![(sov_penpal_on_relay.into(), amount_to_send * 2)]);
|
||||
|
||||
let fee_asset_id: AssetId = Parent.into();
|
||||
PenpalA::execute_with(|| {
|
||||
let result = <PenpalA as PenpalAPallet>::PezkuwiXcm::transfer_assets(
|
||||
<PenpalA as Chain>::RuntimeOrigin::signed(PenpalASender::get()),
|
||||
bx!(destination.into()),
|
||||
bx!(beneficiary.into()),
|
||||
bx!(assets.into()),
|
||||
bx!(fee_asset_id.into()),
|
||||
WeightLimit::Unlimited,
|
||||
);
|
||||
|
||||
// This should fail because ZGR reserve transfer is blocked.
|
||||
assert_err!(
|
||||
result,
|
||||
DispatchError::Module(pezsp_runtime::ModuleError {
|
||||
index: 31,
|
||||
error: [21, 0, 0, 0], // InvalidAssetUnknownReserve.
|
||||
message: Some("InvalidAssetUnknownReserve")
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// Test that `transfer_assets` fails when doing reserve transfer of ZGR from Zagros to PenpalA
|
||||
/// This fails because Zagros's configuration would make this a reserve transfer, which we block.
|
||||
#[test]
|
||||
fn transfer_assets_wnd_reserve_transfer_relay_to_para_fails() {
|
||||
let destination = Zagros::child_location_of(PenpalA::para_id());
|
||||
let beneficiary: Location =
|
||||
AccountId32Junction { network: None, id: PenpalAReceiver::get().into() }.into();
|
||||
let amount_to_send: Balance = ZAGROS_ED * 1000;
|
||||
let assets: Assets = (Here, amount_to_send).into();
|
||||
|
||||
let fee_asset_id: AssetId = Here.into();
|
||||
Zagros::execute_with(|| {
|
||||
let result = <Zagros as ZagrosPallet>::XcmPallet::transfer_assets(
|
||||
<Zagros as Chain>::RuntimeOrigin::signed(ZagrosSender::get()),
|
||||
bx!(destination.into()),
|
||||
bx!(beneficiary.into()),
|
||||
bx!(assets.into()),
|
||||
bx!(fee_asset_id.into()),
|
||||
WeightLimit::Unlimited,
|
||||
);
|
||||
|
||||
// This should fail because ZGR reserve transfer is blocked.
|
||||
assert_err!(
|
||||
result,
|
||||
DispatchError::Module(pezsp_runtime::ModuleError {
|
||||
index: 99,
|
||||
error: [21, 0, 0, 0], // InvalidAssetUnknownReserve.
|
||||
message: Some("InvalidAssetUnknownReserve")
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// ==================================================================================
|
||||
// ============================== PenpalA <-> PenpalB ===============================
|
||||
// ==================================================================================
|
||||
|
||||
/// Test that `transfer_assets` fails when doing reserve transfer of ZGR from PenpalA to PenpalB
|
||||
#[test]
|
||||
fn transfer_assets_wnd_reserve_transfer_para_to_para_fails() {
|
||||
let destination = PenpalA::sibling_location_of(PenpalB::para_id());
|
||||
let beneficiary: Location =
|
||||
AccountId32Junction { network: None, id: PenpalBReceiver::get().into() }.into();
|
||||
let amount_to_send: Balance = ZAGROS_ED * 1000;
|
||||
let assets: Assets = (Parent, amount_to_send).into();
|
||||
|
||||
// Mint ZGR on PenpalA for testing
|
||||
PenpalA::mint_foreign_asset(
|
||||
<PenpalA as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get()),
|
||||
RelayLocation::get(),
|
||||
PenpalASender::get(),
|
||||
amount_to_send * 2,
|
||||
);
|
||||
|
||||
let fee_asset_id: AssetId = Parent.into();
|
||||
PenpalA::execute_with(|| {
|
||||
let result = <PenpalA as PenpalAPallet>::PezkuwiXcm::transfer_assets(
|
||||
<PenpalA as Chain>::RuntimeOrigin::signed(PenpalASender::get()),
|
||||
bx!(destination.into()),
|
||||
bx!(beneficiary.into()),
|
||||
bx!(assets.into()),
|
||||
bx!(fee_asset_id.into()),
|
||||
WeightLimit::Unlimited,
|
||||
);
|
||||
|
||||
// This should fail because ZGR reserve transfer is blocked
|
||||
assert_err!(
|
||||
result,
|
||||
DispatchError::Module(pezsp_runtime::ModuleError {
|
||||
index: 31,
|
||||
error: [21, 0, 0, 0], // InvalidAssetUnknownReserve
|
||||
message: Some("InvalidAssetUnknownReserve")
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// ==================================================================================
|
||||
// ============================== Mixed Assets and Fees =============================
|
||||
// ==================================================================================
|
||||
|
||||
/// Test that `transfer_assets` fails when ZGR is used as fee asset in reserve transfer
|
||||
#[test]
|
||||
fn transfer_assets_wnd_as_fee_in_reserve_transfer_fails() {
|
||||
let destination = PenpalA::sibling_location_of(PenpalB::para_id());
|
||||
let beneficiary: Location =
|
||||
AccountId32Junction { network: None, id: PenpalBReceiver::get().into() }.into();
|
||||
let asset_amount: Balance = 1_000_000_000_000; // A million USDT.
|
||||
let fee_amount: Balance = ZAGROS_ED * 100;
|
||||
|
||||
// Create a foreign asset location (representing another asset).
|
||||
let foreign_asset_location = Location::new(
|
||||
1,
|
||||
[
|
||||
Teyrchain(AssetHubZagros::para_id().into()),
|
||||
PalletInstance(ASSETS_PALLET_ID),
|
||||
GeneralIndex(USDT_ID.into()), // USDT.
|
||||
],
|
||||
);
|
||||
|
||||
// Mint both assets on PenpalA for testing.
|
||||
PenpalA::mint_foreign_asset(
|
||||
<PenpalA as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get()),
|
||||
foreign_asset_location.clone(),
|
||||
PenpalASender::get(),
|
||||
asset_amount * 2,
|
||||
);
|
||||
PenpalA::mint_foreign_asset(
|
||||
<PenpalA as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get()),
|
||||
RelayLocation::get(),
|
||||
PenpalASender::get(),
|
||||
fee_amount * 2,
|
||||
);
|
||||
|
||||
// Transfer foreign asset, pay fees with ZGR.
|
||||
let assets: Assets = vec![
|
||||
(foreign_asset_location, asset_amount).into(),
|
||||
(Parent, fee_amount).into(), // ZGR as fee.
|
||||
]
|
||||
.into();
|
||||
let fee_asset_id: AssetId = Parent.into(); // ZGR is the fee asset.
|
||||
|
||||
PenpalA::execute_with(|| {
|
||||
let result = <PenpalA as PenpalAPallet>::PezkuwiXcm::transfer_assets(
|
||||
<PenpalA as Chain>::RuntimeOrigin::signed(PenpalASender::get()),
|
||||
bx!(destination.into()),
|
||||
bx!(beneficiary.into()),
|
||||
bx!(assets.into()),
|
||||
bx!(fee_asset_id.into()),
|
||||
WeightLimit::Unlimited,
|
||||
);
|
||||
|
||||
// This should fail because ZGR fee would be reserve transferred.
|
||||
assert_err!(
|
||||
result,
|
||||
DispatchError::Module(pezsp_runtime::ModuleError {
|
||||
index: 31,
|
||||
error: [21, 0, 0, 0], // InvalidAssetUnknownReserve.
|
||||
message: Some("InvalidAssetUnknownReserve")
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// Test that `transfer_assets` works when neither asset nor fee is ZGR.
|
||||
#[test]
|
||||
fn transfer_assets_non_native_assets_work() {
|
||||
let destination = PenpalA::sibling_location_of(PenpalB::para_id());
|
||||
let beneficiary: Location =
|
||||
AccountId32Junction { network: None, id: PenpalBReceiver::get().into() }.into();
|
||||
let amount: Balance = 1_000_000_000_000; // A million USDT.
|
||||
|
||||
// Create foreign asset locations (both non-native).
|
||||
let asset_location = Location::new(
|
||||
1,
|
||||
[
|
||||
Teyrchain(AssetHubZagros::para_id().into()),
|
||||
PalletInstance(ASSETS_PALLET_ID),
|
||||
GeneralIndex(USDT_ID.into()), // USDT.
|
||||
],
|
||||
);
|
||||
|
||||
// Mint both USDT and ZGR on PenpalA, one for sending, the other for paying delivery fees.
|
||||
PenpalA::mint_foreign_asset(
|
||||
<PenpalA as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get()),
|
||||
RelayLocation::get(),
|
||||
PenpalASender::get(),
|
||||
amount * 2,
|
||||
);
|
||||
PenpalA::mint_foreign_asset(
|
||||
<PenpalA as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get()),
|
||||
asset_location.clone(),
|
||||
PenpalASender::get(),
|
||||
amount * 2,
|
||||
);
|
||||
|
||||
// Transfer non-native assets.
|
||||
let assets: Assets = (asset_location.clone(), amount).into();
|
||||
let fee_asset_id: AssetId = (asset_location).into();
|
||||
|
||||
PenpalA::execute_with(|| {
|
||||
let result = <PenpalA as PenpalAPallet>::PezkuwiXcm::transfer_assets(
|
||||
<PenpalA as Chain>::RuntimeOrigin::signed(PenpalASender::get()),
|
||||
bx!(destination.into()),
|
||||
bx!(beneficiary.into()),
|
||||
bx!(assets.into()),
|
||||
bx!(fee_asset_id.into()),
|
||||
WeightLimit::Unlimited,
|
||||
);
|
||||
|
||||
// This should succeed because neither asset is ZGR.
|
||||
assert_ok!(result);
|
||||
});
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
// 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::traits::fungibles::{Inspect, Mutate};
|
||||
use pezkuwi_runtime_common::impls::VersionedLocatableAsset;
|
||||
use xcm_executor::traits::ConvertLocation;
|
||||
use zagros_system_emulated_network::zagros_emulated_chain::zagros_runtime::Dmp;
|
||||
|
||||
#[test]
|
||||
fn create_and_claim_treasury_spend() {
|
||||
const SPEND_AMOUNT: u128 = 1_000_000_000;
|
||||
// treasury location from a sibling teyrchain.
|
||||
let treasury_location: Location = Location::new(1, PalletInstance(37));
|
||||
// treasury account on a sibling teyrchain.
|
||||
let treasury_account =
|
||||
ahw_xcm_config::LocationToAccountId::convert_location(&treasury_location).unwrap();
|
||||
let asset_hub_location = Location::new(0, Teyrchain(AssetHubZagros::para_id().into()));
|
||||
let root = <Zagros 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 = Zagros::account_id_of(ALICE);
|
||||
let bob: AccountId = Zagros::account_id_of(BOB);
|
||||
let bob_signed = <Zagros as Chain>::RuntimeOrigin::signed(bob.clone());
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type Assets = <AssetHubZagros as AssetHubZagrosPallet>::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 Inspect<_>>::balance(USDT_ID, &alice,), 0u128,);
|
||||
});
|
||||
|
||||
Zagros::execute_with(|| {
|
||||
type RuntimeEvent = <Zagros as Chain>::RuntimeEvent;
|
||||
type Treasury = <Zagros as ZagrosPallet>::Treasury;
|
||||
type AssetRate = <Zagros as ZagrosPallet>::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!(
|
||||
Zagros,
|
||||
vec![
|
||||
RuntimeEvent::Treasury(pezpallet_treasury::Event::Paid { .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
type Assets = <AssetHubZagros as AssetHubZagrosPallet>::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!(
|
||||
AssetHubZagros,
|
||||
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 Inspect<_>>::balance(USDT_ID, &alice,), SPEND_AMOUNT,);
|
||||
});
|
||||
|
||||
Zagros::execute_with(|| {
|
||||
type RuntimeEvent = <Zagros as Chain>::RuntimeEvent;
|
||||
type Treasury = <Zagros as ZagrosPallet>::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!(
|
||||
Zagros,
|
||||
vec![
|
||||
RuntimeEvent::Treasury(pezpallet_treasury::Event::SpendProcessed { .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
+474
@@ -0,0 +1,474 @@
|
||||
// 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 to ensure correct XCM fee estimation for cross-chain asset transfers.
|
||||
|
||||
use crate::{create_pool_with_wnd_on, 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 usdt_transfer_call(
|
||||
destination: Location,
|
||||
beneficiary: Location,
|
||||
amount_to_send: u128,
|
||||
usdt_location_on_penpal: Location,
|
||||
usdt_location_on_ah: Location,
|
||||
) -> <PenpalA as Chain>::RuntimeCall {
|
||||
let asset_hub_location: Location = PenpalA::sibling_location_of(AssetHubZagros::para_id());
|
||||
|
||||
// Create the XCM to transfer USDT to PenpalB via Asset Hub using InitiateTransfer
|
||||
let remote_xcm_on_penpal_b =
|
||||
Xcm::<()>(vec![DepositAsset { assets: Wild(AllCounted(1)), beneficiary }]);
|
||||
|
||||
let xcm_on_asset_hub = Xcm::<()>(vec![InitiateTransfer {
|
||||
destination,
|
||||
remote_fees: Some(AssetTransferFilter::ReserveDeposit(
|
||||
Definite((usdt_location_on_ah, 1_000_000u128).into()), // 1 USDT for fees
|
||||
)),
|
||||
preserve_origin: false,
|
||||
assets: BoundedVec::truncate_from(vec![AssetTransferFilter::ReserveDeposit(Wild(All))]),
|
||||
remote_xcm: remote_xcm_on_penpal_b,
|
||||
}]);
|
||||
|
||||
let xcm = Xcm::<<PenpalA as Chain>::RuntimeCall>(vec![
|
||||
WithdrawAsset((usdt_location_on_penpal.clone(), amount_to_send).into()),
|
||||
PayFees {
|
||||
asset: Asset {
|
||||
id: AssetId(usdt_location_on_penpal.clone()),
|
||||
fun: Fungible(1_000_000u128), // 1 USDT for local fees
|
||||
},
|
||||
},
|
||||
InitiateTransfer {
|
||||
destination: asset_hub_location,
|
||||
remote_fees: Some(AssetTransferFilter::ReserveWithdraw(
|
||||
Definite((usdt_location_on_penpal.clone(), 1_000_000u128).into()), /* 1 USDT for
|
||||
* Asset Hub fees */
|
||||
)),
|
||||
preserve_origin: false,
|
||||
assets: BoundedVec::truncate_from(vec![AssetTransferFilter::ReserveWithdraw(Wild(
|
||||
All,
|
||||
))]),
|
||||
remote_xcm: xcm_on_asset_hub,
|
||||
},
|
||||
]);
|
||||
|
||||
<PenpalA as Chain>::RuntimeCall::PezkuwiXcm(pezpallet_xcm::Call::execute {
|
||||
message: bx!(VersionedXcm::from(xcm)),
|
||||
max_weight: Weight::MAX,
|
||||
})
|
||||
}
|
||||
|
||||
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 = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
AssetHubZagros::assert_xcmp_queue_success(None);
|
||||
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
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 = PenpalA::sibling_location_of(AssetHubZagros::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(test.args.fee_asset_id)),
|
||||
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 ZGR 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 relay_native_asset_location = Location::parent();
|
||||
let sender_as_seen_by_ah = AssetHubZagros::sibling_location_of(PenpalA::para_id());
|
||||
let sov_of_sender_on_ah = AssetHubZagros::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.
|
||||
AssetHubZagros::fund_accounts(vec![(sov_of_sender_on_ah.clone(), amount_to_send * 2)]);
|
||||
|
||||
// Init values for Teyrchain Destination
|
||||
let beneficiary_id = PenpalBReceiver::get();
|
||||
|
||||
let fee_asset_id: AssetId = Parent.into();
|
||||
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()));
|
||||
<AssetHubZagros as TestExt>::execute_with(|| {
|
||||
type Runtime = <AssetHubZagros as Chain>::Runtime;
|
||||
type RuntimeCall = <AssetHubZagros 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(Location::parent()))
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
// Dry-running is done.
|
||||
PenpalA::reset_ext();
|
||||
AssetHubZagros::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,
|
||||
);
|
||||
AssetHubZagros::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::<AssetHubZagros>(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,
|
||||
AssetHubZagros,
|
||||
PenpalB,
|
||||
(Parent, 1_000_000_000_000u128),
|
||||
Penpal
|
||||
);
|
||||
}
|
||||
|
||||
/// We are able to estimate delivery fees in USDT for a USDT transfer from PenpalA to PenpalB via
|
||||
/// Asset Hub. Scenario: Alice on PenpalA has some USDT and wants to send them to PenpalB.
|
||||
/// We want to estimate the delivery fees in USDT using the new `asset_id` parameter in
|
||||
/// `query_delivery_fees`.
|
||||
#[test]
|
||||
fn usdt_fee_estimation_in_usdt_works() {
|
||||
use emulated_integration_tests_common::USDT_ID;
|
||||
|
||||
let destination = PenpalA::sibling_location_of(PenpalB::para_id());
|
||||
let sender = PenpalASender::get();
|
||||
let amount_to_send = 10_000_000; // 10 USDT (6 decimals)
|
||||
|
||||
// USDT location from PenpalA's perspective
|
||||
let usdt_location_on_penpal = PenpalUsdtFromAssetHub::get();
|
||||
|
||||
// USDT location from Asset Hub's perspective
|
||||
let usdt_location_on_ah =
|
||||
Location::new(0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(USDT_ID.into())]);
|
||||
|
||||
let penpal_as_seen_by_ah = AssetHubZagros::sibling_location_of(PenpalA::para_id());
|
||||
let sov_of_penpal_on_ah = AssetHubZagros::sovereign_account_id_of(penpal_as_seen_by_ah.clone());
|
||||
|
||||
// fund PenpalA's sender account with USDT
|
||||
PenpalA::mint_foreign_asset(
|
||||
<PenpalA as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get()),
|
||||
usdt_location_on_penpal.clone(),
|
||||
sender.clone(),
|
||||
amount_to_send * 2,
|
||||
);
|
||||
|
||||
// fund PenpalA's sovereign account on AssetHub with USDT
|
||||
AssetHubZagros::mint_asset(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(AssetHubZagrosAssetOwner::get()),
|
||||
USDT_ID,
|
||||
sov_of_penpal_on_ah.clone(),
|
||||
amount_to_send * 2,
|
||||
);
|
||||
|
||||
// Create a liquidity pool between ZGR (relay token) and USDT on AssetHub
|
||||
// This is needed for the asset conversion in fee estimation
|
||||
create_pool_with_wnd_on!(
|
||||
AssetHubZagros,
|
||||
usdt_location_on_ah.clone(),
|
||||
false,
|
||||
AssetHubZagrosSender::get(),
|
||||
1_000_000_000_000, // 1 ZGR
|
||||
2_000_000 // 2 USDT (1:2 ratio)
|
||||
);
|
||||
|
||||
// Create a liquidity pool between ZGR and USDT on PenpalA as well
|
||||
// This is needed for PenpalA to perform asset conversion for fee estimation
|
||||
create_pool_with_wnd_on!(
|
||||
PenpalA,
|
||||
usdt_location_on_penpal.clone(),
|
||||
true,
|
||||
PenpalAssetOwner::get(),
|
||||
1_000_000_000_000, // 1 ZGR
|
||||
2_000_000 // 2 USDT (1:2 ratio)
|
||||
);
|
||||
|
||||
let beneficiary_id = PenpalBReceiver::get();
|
||||
|
||||
// We get the delivery fees 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 = usdt_transfer_call(
|
||||
destination.clone(),
|
||||
beneficiary_id.clone().into(),
|
||||
amount_to_send,
|
||||
usdt_location_on_penpal.clone(),
|
||||
usdt_location_on_ah.clone(),
|
||||
);
|
||||
|
||||
let asset_hub_location: Location = PenpalA::sibling_location_of(AssetHubZagros::para_id());
|
||||
|
||||
let origin = OriginCaller::system(RawOrigin::Signed(sender.clone()));
|
||||
let result = Runtime::dry_run_call(origin, call, xcm::prelude::XCM_VERSION).unwrap();
|
||||
|
||||
// Find the message sent to Asset Hub
|
||||
let (destination_to_query, messages_to_query) = &result
|
||||
.forwarded_xcms
|
||||
.iter()
|
||||
.find(|(destination, _)| {
|
||||
*destination == VersionedLocation::from(asset_hub_location.clone())
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(messages_to_query.len(), 1);
|
||||
remote_message = messages_to_query[0].clone();
|
||||
|
||||
// Query delivery fees in USDT using the new asset_id parameter
|
||||
let usdt_asset_id = VersionedAssetId::from(AssetId(usdt_location_on_penpal.clone()));
|
||||
let delivery_fees = Runtime::query_delivery_fees(
|
||||
destination_to_query.clone(),
|
||||
remote_message.clone(),
|
||||
usdt_asset_id,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
delivery_fees_amount = get_amount_from_versioned_assets(delivery_fees.clone());
|
||||
|
||||
// Verify the fees are quoted in USDT (the delivery fees should be converted from native to
|
||||
// USDT)
|
||||
let fee_assets = match delivery_fees {
|
||||
VersionedAssets::V5(assets) => assets,
|
||||
_ => panic!("Expected V5 assets"),
|
||||
};
|
||||
|
||||
// Should have one asset (USDT)
|
||||
assert_eq!(fee_assets.len(), 1);
|
||||
let fee_asset = fee_assets.get(0).unwrap();
|
||||
|
||||
// Verify it's USDT
|
||||
assert_eq!(fee_asset.id.0, usdt_location_on_penpal);
|
||||
|
||||
// Verify we get a reasonable USDT amount (delivery fees should be > 0)
|
||||
if let Fungible(amount) = fee_asset.fun {
|
||||
assert!(amount > 0, "Delivery fees should be greater than 0");
|
||||
} else {
|
||||
panic!("Expected fungible delivery fees");
|
||||
}
|
||||
});
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
[package]
|
||||
name = "bridge-hub-pezkuwichain-integration-tests"
|
||||
version = "1.0.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license = "Apache-2.0"
|
||||
description = "Bridge Hub Pezkuwichain runtime integration tests with xcm-emulator"
|
||||
publish = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
codec = { workspace = true }
|
||||
hex-literal = { workspace = true, default-features = true }
|
||||
scale-info = { features = ["derive"], workspace = true }
|
||||
|
||||
# Bizinikiwi
|
||||
pezframe-support = { workspace = true }
|
||||
pezpallet-asset-conversion = { workspace = true }
|
||||
pezpallet-assets = { workspace = true }
|
||||
pezpallet-balances = { workspace = true }
|
||||
pezpallet-message-queue = { workspace = true, default-features = true }
|
||||
pezsp-core = { workspace = true }
|
||||
pezsp-runtime = { workspace = true }
|
||||
|
||||
# Pezkuwi
|
||||
pezpallet-xcm = { workspace = true }
|
||||
xcm = { workspace = true }
|
||||
xcm-builder = { workspace = true }
|
||||
xcm-executor = { workspace = true }
|
||||
|
||||
# Bridges
|
||||
bp-asset-hub-pezkuwichain = { workspace = true }
|
||||
pezpallet-bridge-messages = { workspace = true }
|
||||
|
||||
# Pezcumulus
|
||||
asset-hub-pezkuwichain-runtime = { workspace = true }
|
||||
pezcumulus-pezpallet-xcmp-queue = { workspace = true }
|
||||
emulated-integration-tests-common = { workspace = true }
|
||||
pezkuwichain-system-emulated-network = { workspace = true }
|
||||
pezkuwichain-zagros-system-emulated-network = { workspace = true }
|
||||
testnet-teyrchains-constants = { features = [
|
||||
"pezkuwichain",
|
||||
"zagros",
|
||||
], workspace = true, default-features = true }
|
||||
teyrchains-common = { workspace = true, default-features = true }
|
||||
|
||||
# Snowbridge
|
||||
snowbridge-inbound-queue-primitives = { workspace = true }
|
||||
snowbridge-outbound-queue-primitives = { workspace = true }
|
||||
snowbridge-pezpallet-inbound-queue-fixtures = { workspace = true, default-features = true }
|
||||
snowbridge-pezpallet-outbound-queue = { workspace = true }
|
||||
snowbridge-pezpallet-system = { workspace = true }
|
||||
|
||||
[features]
|
||||
runtime-benchmarks = [
|
||||
"asset-hub-pezkuwichain-runtime/runtime-benchmarks",
|
||||
"bp-asset-hub-pezkuwichain/runtime-benchmarks",
|
||||
"pezcumulus-pezpallet-xcmp-queue/runtime-benchmarks",
|
||||
"emulated-integration-tests-common/runtime-benchmarks",
|
||||
"pezframe-support/runtime-benchmarks",
|
||||
"pezpallet-asset-conversion/runtime-benchmarks",
|
||||
"pezpallet-assets/runtime-benchmarks",
|
||||
"pezpallet-balances/runtime-benchmarks",
|
||||
"pezpallet-bridge-messages/runtime-benchmarks",
|
||||
"pezpallet-message-queue/runtime-benchmarks",
|
||||
"pezpallet-xcm/runtime-benchmarks",
|
||||
"pezkuwichain-system-emulated-network/runtime-benchmarks",
|
||||
"pezkuwichain-zagros-system-emulated-network/runtime-benchmarks",
|
||||
"snowbridge-inbound-queue-primitives/runtime-benchmarks",
|
||||
"snowbridge-outbound-queue-primitives/runtime-benchmarks",
|
||||
"snowbridge-pezpallet-inbound-queue-fixtures/runtime-benchmarks",
|
||||
"snowbridge-pezpallet-outbound-queue/runtime-benchmarks",
|
||||
"snowbridge-pezpallet-system/runtime-benchmarks",
|
||||
"pezsp-runtime/runtime-benchmarks",
|
||||
"testnet-teyrchains-constants/runtime-benchmarks",
|
||||
"teyrchains-common/runtime-benchmarks",
|
||||
"xcm-builder/runtime-benchmarks",
|
||||
"xcm-executor/runtime-benchmarks",
|
||||
"xcm/runtime-benchmarks",
|
||||
]
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
// 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 {
|
||||
// Bizinikiwi
|
||||
pub(crate) use codec::Encode;
|
||||
pub(crate) use pezframe_support::{assert_err, assert_ok, pezpallet_prelude::DispatchResult};
|
||||
pub(crate) use pezsp_runtime::DispatchError;
|
||||
|
||||
// Pezkuwi
|
||||
pub(crate) use xcm::{
|
||||
latest::{ParentThen, PEZKUWICHAIN_GENESIS_HASH, ZAGROS_GENESIS_HASH},
|
||||
prelude::{AccountId32 as AccountId32Junction, *},
|
||||
};
|
||||
pub(crate) use xcm_builder::ExternalConsensusLocationsConverterFor;
|
||||
pub(crate) use xcm_executor::traits::TransferType;
|
||||
|
||||
// Pezcumulus
|
||||
pub(crate) use emulated_integration_tests_common::{
|
||||
accounts::ALICE,
|
||||
impls::Inspect,
|
||||
test_dry_run_transfer_across_pk_bridge, test_relay_is_trusted_teleporter,
|
||||
test_teyrchain_is_trusted_teleporter, test_teyrchain_is_trusted_teleporter_for_relay,
|
||||
xcm_emulator::{
|
||||
assert_expected_events, bx, Chain, RelayChain as Relay, TestExt, Teyrchain as Para,
|
||||
},
|
||||
xcm_helpers::xcm_transact_paid_execution,
|
||||
ASSETS_PALLET_ID, USDT_ID,
|
||||
};
|
||||
pub(crate) use pezkuwichain_zagros_system_emulated_network::{
|
||||
asset_hub_pezkuwichain_emulated_chain::{
|
||||
asset_hub_pezkuwichain_runtime::{
|
||||
xcm_config::TreasuryAccount, ForeignAssetReserveData,
|
||||
},
|
||||
genesis::ED as ASSET_HUB_PEZKUWICHAIN_ED,
|
||||
AssetHubPezkuwichainParaPallet as AssetHubPezkuwichainPallet,
|
||||
},
|
||||
asset_hub_zagros_emulated_chain::{
|
||||
genesis::{AssetHubZagrosAssetOwner, ED as ASSET_HUB_ZAGROS_ED},
|
||||
AssetHubZagrosParaPallet as AssetHubZagrosPallet,
|
||||
},
|
||||
bridge_hub_pezkuwichain_emulated_chain::{
|
||||
genesis::ED as BRIDGE_HUB_PEZKUWICHAIN_ED, BridgeHubPezkuwichainExistentialDeposit,
|
||||
BridgeHubPezkuwichainParaPallet as BridgeHubPezkuwichainPallet,
|
||||
},
|
||||
penpal_emulated_chain::{
|
||||
penpal_runtime::xcm_config::{
|
||||
CustomizableAssetFromSystemAssetHub as PenpalCustomizableAssetFromSystemAssetHub,
|
||||
UniversalLocation as PenpalUniversalLocation,
|
||||
},
|
||||
PenpalAParaPallet as PenpalAPallet, PenpalAssetOwner,
|
||||
},
|
||||
pezkuwichain_emulated_chain::{
|
||||
genesis::ED as PEZKUWICHAIN_ED, PezkuwichainRelayPallet as PezkuwichainPallet,
|
||||
},
|
||||
AssetHubPezkuwichainPara as AssetHubPezkuwichain,
|
||||
AssetHubPezkuwichainParaReceiver as AssetHubPezkuwichainReceiver,
|
||||
AssetHubPezkuwichainParaSender as AssetHubPezkuwichainSender,
|
||||
AssetHubZagrosPara as AssetHubZagros, AssetHubZagrosParaReceiver as AssetHubZagrosReceiver,
|
||||
AssetHubZagrosParaSender as AssetHubZagrosSender,
|
||||
BridgeHubPezkuwichainPara as BridgeHubPezkuwichain,
|
||||
BridgeHubPezkuwichainParaReceiver as BridgeHubPezkuwichainReceiver,
|
||||
BridgeHubPezkuwichainParaSender as BridgeHubPezkuwichainSender,
|
||||
BridgeHubZagrosPara as BridgeHubZagros, PenpalAPara as PenpalA,
|
||||
PenpalAParaSender as PenpalASender, PezkuwichainRelay as Pezkuwichain,
|
||||
PezkuwichainRelayReceiver as PezkuwichainReceiver,
|
||||
PezkuwichainRelaySender as PezkuwichainSender,
|
||||
};
|
||||
pub(crate) use teyrchains_common::AccountId;
|
||||
|
||||
pub(crate) const ASSET_ID: u32 = 1;
|
||||
pub(crate) const ASSET_MIN_BALANCE: u128 = 1000;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
+617
@@ -0,0 +1,617 @@
|
||||
// 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::tests::*;
|
||||
|
||||
fn send_assets_over_bridge<F: FnOnce()>(send_fn: F) {
|
||||
// fund the AHR's SA on BHR for paying bridge delivery fees
|
||||
BridgeHubPezkuwichain::fund_para_sovereign(
|
||||
AssetHubPezkuwichain::para_id(),
|
||||
10_000_000_000_000u128,
|
||||
);
|
||||
|
||||
// set XCM versions
|
||||
let local_asset_hub = PenpalA::sibling_location_of(AssetHubPezkuwichain::para_id());
|
||||
PenpalA::force_xcm_version(local_asset_hub.clone(), XCM_VERSION);
|
||||
AssetHubPezkuwichain::force_xcm_version(asset_hub_zagros_location(), XCM_VERSION);
|
||||
BridgeHubPezkuwichain::force_xcm_version(bridge_hub_zagros_location(), XCM_VERSION);
|
||||
|
||||
// send message over bridge
|
||||
send_fn();
|
||||
|
||||
// process and verify intermediary hops
|
||||
assert_bridge_hub_pezkuwichain_message_accepted(true);
|
||||
assert_bridge_hub_zagros_message_received();
|
||||
}
|
||||
|
||||
fn set_up_rocs_for_penpal_pezkuwichain_through_ahr_to_ahw(
|
||||
sender: &AccountId,
|
||||
amount: u128,
|
||||
) -> (Location, v5::Location) {
|
||||
let roc_at_pezkuwichain_teyrchains = roc_at_ah_pezkuwichain();
|
||||
let roc_at_asset_hub_zagros = bridged_roc_at_ah_zagros();
|
||||
let reserves = vec![(asset_hub_pezkuwichain_global_location(), false).into()];
|
||||
create_foreign_on_ah_zagros(roc_at_asset_hub_zagros.clone(), true, reserves);
|
||||
|
||||
let penpal_location = AssetHubPezkuwichain::sibling_location_of(PenpalA::para_id());
|
||||
let sov_penpal_on_ahr = AssetHubPezkuwichain::sovereign_account_id_of(penpal_location);
|
||||
// fund Penpal's sovereign account on AssetHub
|
||||
AssetHubPezkuwichain::fund_accounts(vec![(sov_penpal_on_ahr.into(), amount * 2)]);
|
||||
// fund Penpal's sender account
|
||||
PenpalA::mint_foreign_asset(
|
||||
<PenpalA as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get()),
|
||||
roc_at_pezkuwichain_teyrchains.clone(),
|
||||
sender.clone(),
|
||||
amount * 2,
|
||||
);
|
||||
(roc_at_pezkuwichain_teyrchains, roc_at_asset_hub_zagros)
|
||||
}
|
||||
|
||||
fn send_assets_from_penpal_pezkuwichain_through_pezkuwichain_ah_to_zagros_ah(
|
||||
destination: Location,
|
||||
assets: (Assets, TransferType),
|
||||
fees: (AssetId, TransferType),
|
||||
custom_xcm_on_dest: Xcm<()>,
|
||||
) {
|
||||
send_assets_over_bridge(|| {
|
||||
let sov_penpal_on_ahr = AssetHubPezkuwichain::sovereign_account_id_of(
|
||||
AssetHubPezkuwichain::sibling_location_of(PenpalA::para_id()),
|
||||
);
|
||||
// send message over bridge
|
||||
assert_ok!(PenpalA::execute_with(|| {
|
||||
let signed_origin = <PenpalA as Chain>::RuntimeOrigin::signed(PenpalASender::get());
|
||||
<PenpalA as PenpalAPallet>::PezkuwiXcm::transfer_assets_using_type_and_then(
|
||||
signed_origin,
|
||||
bx!(destination.into()),
|
||||
bx!(assets.0.into()),
|
||||
bx!(assets.1),
|
||||
bx!(fees.0.into()),
|
||||
bx!(fees.1),
|
||||
bx!(VersionedXcm::from(custom_xcm_on_dest)),
|
||||
WeightLimit::Unlimited,
|
||||
)
|
||||
}));
|
||||
// verify intermediary AH Pezkuwichain hop
|
||||
AssetHubPezkuwichain::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubPezkuwichain as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubPezkuwichain,
|
||||
vec![
|
||||
// Amount to reserve transfer is withdrawn from Penpal's sovereign account
|
||||
RuntimeEvent::Balances(
|
||||
pezpallet_balances::Event::Burned { who, .. }
|
||||
) => {
|
||||
who: *who == sov_penpal_on_ahr.clone().into(),
|
||||
},
|
||||
// Amount deposited in AHW's sovereign account
|
||||
RuntimeEvent::Balances(pezpallet_balances::Event::Minted { who, .. }) => {
|
||||
who: *who == TreasuryAccount::get(),
|
||||
},
|
||||
RuntimeEvent::XcmpQueue(
|
||||
cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// Test transfer of TYR from AssetHub Pezkuwichain to AssetHub Zagros.
|
||||
fn send_roc_from_asset_hub_pezkuwichain_to_asset_hub_zagros() {
|
||||
let amount = ASSET_HUB_PEZKUWICHAIN_ED * 1_000_000;
|
||||
let sender = AssetHubPezkuwichainSender::get();
|
||||
let receiver = AssetHubZagrosReceiver::get();
|
||||
let roc_at_asset_hub_pezkuwichain = roc_at_ah_pezkuwichain();
|
||||
let bridged_roc_at_asset_hub_zagros = bridged_roc_at_ah_zagros();
|
||||
let reserves = vec![(asset_hub_pezkuwichain_global_location(), false).into()];
|
||||
create_foreign_on_ah_zagros(bridged_roc_at_asset_hub_zagros.clone(), true, reserves);
|
||||
set_up_pool_with_wnd_on_ah_zagros(bridged_roc_at_asset_hub_zagros.clone(), true);
|
||||
|
||||
let sov_ahw_on_ahr =
|
||||
AssetHubPezkuwichain::sovereign_account_of_teyrchain_on_other_global_consensus(
|
||||
ByGenesis(ZAGROS_GENESIS_HASH),
|
||||
AssetHubZagros::para_id(),
|
||||
);
|
||||
let rocs_in_reserve_on_ahr_before =
|
||||
<AssetHubPezkuwichain as Chain>::account_data_of(sov_ahw_on_ahr.clone()).free;
|
||||
let sender_rocs_before = <AssetHubPezkuwichain as Chain>::account_data_of(sender.clone()).free;
|
||||
let receiver_rocs_before =
|
||||
foreign_balance_on_ah_zagros(bridged_roc_at_asset_hub_zagros.clone(), &receiver);
|
||||
|
||||
// send TYRs, use them for fees
|
||||
send_assets_over_bridge(|| {
|
||||
let destination = asset_hub_zagros_location();
|
||||
let assets: Assets = (roc_at_asset_hub_pezkuwichain.clone(), amount).into();
|
||||
let fee_idx = 0;
|
||||
let transfer_type = TransferType::LocalReserve;
|
||||
assert_ok!(send_assets_from_asset_hub_pezkuwichain(
|
||||
destination,
|
||||
assets,
|
||||
fee_idx,
|
||||
transfer_type
|
||||
));
|
||||
});
|
||||
|
||||
// verify expected events on final destination
|
||||
let roc = Location::new(2, [GlobalConsensus(ByGenesis(PEZKUWICHAIN_GENESIS_HASH))]);
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
// issue TYRs on AHW
|
||||
RuntimeEvent::ForeignAssets(pezpallet_assets::Event::Issued { asset_id, owner, .. }) => {
|
||||
asset_id: *asset_id == roc,
|
||||
owner: owner == &receiver,
|
||||
},
|
||||
// message processed successfully
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
let sender_rocs_after = <AssetHubPezkuwichain as Chain>::account_data_of(sender.clone()).free;
|
||||
let receiver_rocs_after =
|
||||
foreign_balance_on_ah_zagros(bridged_roc_at_asset_hub_zagros, &receiver);
|
||||
let rocs_in_reserve_on_ahr_after =
|
||||
<AssetHubPezkuwichain as Chain>::account_data_of(sov_ahw_on_ahr.clone()).free;
|
||||
|
||||
// Sender's TYR balance is reduced
|
||||
assert!(sender_rocs_before > sender_rocs_after);
|
||||
// Receiver's TYR balance is increased
|
||||
assert!(receiver_rocs_after > receiver_rocs_before);
|
||||
// Reserve TYR balance is increased by sent amount
|
||||
assert_eq!(rocs_in_reserve_on_ahr_after, rocs_in_reserve_on_ahr_before + amount);
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// Send bridged assets "back" from AssetHub Pezkuwichain to AssetHub Zagros.
|
||||
///
|
||||
/// This mix of assets should cover the whole range:
|
||||
/// - bridged native assets: TYR,
|
||||
/// - bridged trust-based assets: USDT (exists only on Zagros, Pezkuwichain gets it from Zagros over
|
||||
/// bridge),
|
||||
/// - bridged foreign asset / double-bridged asset (other bridge / Snowfork): wETH (bridged from
|
||||
/// Ethereum to Zagros over Snowbridge, then bridged over to Pezkuwichain through this bridge).
|
||||
fn send_back_wnds_usdt_and_weth_from_asset_hub_pezkuwichain_to_asset_hub_zagros() {
|
||||
let prefund_amount = 10_000_000_000_000u128;
|
||||
let amount_to_send = ASSET_HUB_ZAGROS_ED * 1_000;
|
||||
let sender = AssetHubPezkuwichainSender::get();
|
||||
let receiver = AssetHubZagrosReceiver::get();
|
||||
let wnd_at_asset_hub_pezkuwichain = bridged_wnd_at_ah_pezkuwichain();
|
||||
let prefund_accounts = vec![(sender.clone(), prefund_amount)];
|
||||
let reserves = vec![(asset_hub_zagros_location(), false).into()];
|
||||
create_foreign_on_ah_pezkuwichain(
|
||||
wnd_at_asset_hub_pezkuwichain.clone(),
|
||||
true,
|
||||
reserves,
|
||||
prefund_accounts,
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// Let's first send back just some ZGRs as a simple example
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
// fund the AHR's SA on AHW with the ZGR tokens held in reserve
|
||||
let sov_ahr_on_ahw = AssetHubZagros::sovereign_account_of_teyrchain_on_other_global_consensus(
|
||||
ByGenesis(PEZKUWICHAIN_GENESIS_HASH),
|
||||
AssetHubPezkuwichain::para_id(),
|
||||
);
|
||||
AssetHubZagros::fund_accounts(vec![(sov_ahr_on_ahw.clone(), prefund_amount)]);
|
||||
|
||||
let wnds_in_reserve_on_ahw_before =
|
||||
<AssetHubZagros as Chain>::account_data_of(sov_ahr_on_ahw.clone()).free;
|
||||
assert_eq!(wnds_in_reserve_on_ahw_before, prefund_amount);
|
||||
|
||||
let sender_wnds_before =
|
||||
foreign_balance_on_ah_pezkuwichain(wnd_at_asset_hub_pezkuwichain.clone(), &sender);
|
||||
assert_eq!(sender_wnds_before, prefund_amount);
|
||||
let receiver_wnds_before = <AssetHubZagros as Chain>::account_data_of(receiver.clone()).free;
|
||||
|
||||
// send back WNDs, use them for fees
|
||||
send_assets_over_bridge(|| {
|
||||
let destination = asset_hub_zagros_location();
|
||||
let assets: Assets = (wnd_at_asset_hub_pezkuwichain.clone(), amount_to_send).into();
|
||||
let fee_idx = 0;
|
||||
let transfer_type = TransferType::DestinationReserve;
|
||||
assert_ok!(send_assets_from_asset_hub_pezkuwichain(
|
||||
destination,
|
||||
assets,
|
||||
fee_idx,
|
||||
transfer_type
|
||||
));
|
||||
});
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
// ZGR is withdrawn from AHR's SA on AHW
|
||||
RuntimeEvent::Balances(
|
||||
pezpallet_balances::Event::Burned { who, amount }
|
||||
) => {
|
||||
who: *who == sov_ahr_on_ahw,
|
||||
amount: *amount == amount_to_send,
|
||||
},
|
||||
// ZGRs deposited to beneficiary
|
||||
RuntimeEvent::Balances(pezpallet_balances::Event::Minted { who, .. }) => {
|
||||
who: who == &receiver,
|
||||
},
|
||||
// message processed successfully
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
let sender_wnds_after =
|
||||
foreign_balance_on_ah_pezkuwichain(wnd_at_asset_hub_pezkuwichain, &sender);
|
||||
let receiver_wnds_after = <AssetHubZagros as Chain>::account_data_of(receiver.clone()).free;
|
||||
let wnds_in_reserve_on_ahw_after =
|
||||
<AssetHubZagros as Chain>::account_data_of(sov_ahr_on_ahw).free;
|
||||
|
||||
// Sender's balance is reduced
|
||||
assert!(sender_wnds_before > sender_wnds_after);
|
||||
// Receiver's balance is increased
|
||||
assert!(receiver_wnds_after > receiver_wnds_before);
|
||||
// Reserve balance is reduced by sent amount
|
||||
assert_eq!(wnds_in_reserve_on_ahw_after, wnds_in_reserve_on_ahw_before - amount_to_send);
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// Now let's send back over USDTs + wETH (and pay fees with USDT)
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
// wETH has same relative location on both Zagros and Pezkuwichain AssetHubs
|
||||
let bridged_weth_at_ah = weth_at_asset_hubs();
|
||||
let bridged_usdt_at_asset_hub_pezkuwichain = bridged_usdt_at_ah_pezkuwichain();
|
||||
|
||||
// set up destination chain AH Zagros:
|
||||
// create a ZGR/USDT pool to be able to pay fees with USDT (USDT created in genesis)
|
||||
set_up_pool_with_wnd_on_ah_zagros(usdt_at_ah_zagros(), false);
|
||||
// prefund AHR's sovereign account on AHW to be able to withdraw USDT and wETH from reserves
|
||||
let sov_ahr_on_ahw = AssetHubZagros::sovereign_account_of_teyrchain_on_other_global_consensus(
|
||||
ByGenesis(PEZKUWICHAIN_GENESIS_HASH),
|
||||
AssetHubPezkuwichain::para_id(),
|
||||
);
|
||||
|
||||
let sov_ahw_on_ahr =
|
||||
AssetHubPezkuwichain::sovereign_account_of_teyrchain_on_other_global_consensus(
|
||||
ByGenesis(ZAGROS_GENESIS_HASH),
|
||||
AssetHubZagros::para_id(),
|
||||
);
|
||||
|
||||
AssetHubZagros::mint_asset(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(AssetHubZagrosAssetOwner::get()),
|
||||
USDT_ID,
|
||||
sov_ahr_on_ahw.clone(),
|
||||
amount_to_send * 2,
|
||||
);
|
||||
AssetHubZagros::mint_foreign_asset(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(snowbridge_sovereign()),
|
||||
bridged_weth_at_ah.clone(),
|
||||
sov_ahr_on_ahw.clone(),
|
||||
amount_to_send * 2,
|
||||
);
|
||||
AssetHubPezkuwichain::mint_foreign_asset(
|
||||
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(sov_ahw_on_ahr.clone()),
|
||||
bridged_weth_at_ah.clone(),
|
||||
sov_ahr_on_ahw,
|
||||
prefund_amount,
|
||||
);
|
||||
AssetHubPezkuwichain::mint_foreign_asset(
|
||||
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(sov_ahw_on_ahr),
|
||||
bridged_weth_at_ah.clone(),
|
||||
sender.clone(),
|
||||
prefund_amount,
|
||||
);
|
||||
|
||||
// set up source chain AH Pezkuwichain:
|
||||
// create wETH and USDT foreign assets on Pezkuwichain and prefund sender's account
|
||||
let prefund_accounts = vec![(sender.clone(), amount_to_send * 2)];
|
||||
let reserves = vec![(asset_hub_zagros_location(), false).into()];
|
||||
create_foreign_on_ah_pezkuwichain(
|
||||
bridged_usdt_at_asset_hub_pezkuwichain.clone(),
|
||||
true,
|
||||
reserves,
|
||||
prefund_accounts,
|
||||
);
|
||||
|
||||
// check balances before
|
||||
let receiver_usdts_before = AssetHubZagros::execute_with(|| {
|
||||
type Assets = <AssetHubZagros as AssetHubZagrosPallet>::Assets;
|
||||
<Assets as Inspect<_>>::balance(USDT_ID, &receiver)
|
||||
});
|
||||
let receiver_weth_before = foreign_balance_on_ah_zagros(bridged_weth_at_ah.clone(), &receiver);
|
||||
|
||||
let usdt_id: AssetId =
|
||||
Location::try_from(bridged_usdt_at_asset_hub_pezkuwichain).unwrap().into();
|
||||
// send USDTs and wETHs
|
||||
let assets: Assets = vec![
|
||||
(usdt_id.clone(), amount_to_send).into(),
|
||||
(Location::try_from(bridged_weth_at_ah.clone()).unwrap(), amount_to_send).into(),
|
||||
]
|
||||
.into();
|
||||
// use USDT for fees
|
||||
let fee = usdt_id;
|
||||
|
||||
// use the more involved transfer extrinsic
|
||||
let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset {
|
||||
assets: Wild(AllCounted(assets.len() as u32)),
|
||||
beneficiary: AccountId32Junction { network: None, id: receiver.clone().into() }.into(),
|
||||
}]);
|
||||
assert_ok!(AssetHubPezkuwichain::execute_with(|| {
|
||||
<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::PezkuwiXcm::transfer_assets_using_type_and_then(
|
||||
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(sender.into()),
|
||||
bx!(asset_hub_zagros_location().into()),
|
||||
bx!(assets.into()),
|
||||
bx!(TransferType::DestinationReserve),
|
||||
bx!(fee.into()),
|
||||
bx!(TransferType::DestinationReserve),
|
||||
bx!(VersionedXcm::from(custom_xcm_on_dest)),
|
||||
WeightLimit::Unlimited,
|
||||
)
|
||||
}));
|
||||
// verify hops (also advances the message through the hops)
|
||||
assert_bridge_hub_pezkuwichain_message_accepted(true);
|
||||
assert_bridge_hub_zagros_message_received();
|
||||
AssetHubZagros::execute_with(|| {
|
||||
AssetHubZagros::assert_xcmp_queue_success(None);
|
||||
});
|
||||
|
||||
let receiver_usdts_after = AssetHubZagros::execute_with(|| {
|
||||
type Assets = <AssetHubZagros as AssetHubZagrosPallet>::Assets;
|
||||
<Assets as Inspect<_>>::balance(USDT_ID, &receiver)
|
||||
});
|
||||
let receiver_weth_after = foreign_balance_on_ah_zagros(bridged_weth_at_ah, &receiver);
|
||||
|
||||
// Receiver's USDT balance is increased by almost `amount_to_send` (minus fees)
|
||||
assert!(receiver_usdts_after > receiver_usdts_before);
|
||||
assert!(receiver_usdts_after < receiver_usdts_before + amount_to_send);
|
||||
// Receiver's wETH balance is increased by `amount_to_send`
|
||||
assert_eq!(receiver_weth_after, receiver_weth_before + amount_to_send);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_rocs_from_penpal_pezkuwichain_through_asset_hub_pezkuwichain_to_asset_hub_zagros() {
|
||||
let amount = ASSET_HUB_PEZKUWICHAIN_ED * 10_000_000;
|
||||
let sender = PenpalASender::get();
|
||||
let receiver = AssetHubZagrosReceiver::get();
|
||||
let local_asset_hub = PenpalA::sibling_location_of(AssetHubPezkuwichain::para_id());
|
||||
let (roc_at_pezkuwichain_teyrchains, roc_at_asset_hub_zagros) =
|
||||
set_up_rocs_for_penpal_pezkuwichain_through_ahr_to_ahw(&sender, amount);
|
||||
set_up_pool_with_wnd_on_ah_zagros(roc_at_asset_hub_zagros.clone(), true);
|
||||
|
||||
let sov_ahw_on_ahr =
|
||||
AssetHubPezkuwichain::sovereign_account_of_teyrchain_on_other_global_consensus(
|
||||
ByGenesis(ZAGROS_GENESIS_HASH),
|
||||
AssetHubZagros::para_id(),
|
||||
);
|
||||
let rocs_in_reserve_on_ahr_before =
|
||||
<AssetHubPezkuwichain as Chain>::account_data_of(sov_ahw_on_ahr.clone()).free;
|
||||
let sender_rocs_before = PenpalA::execute_with(|| {
|
||||
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
|
||||
<ForeignAssets as Inspect<_>>::balance(roc_at_pezkuwichain_teyrchains.clone(), &sender)
|
||||
});
|
||||
let receiver_rocs_before =
|
||||
foreign_balance_on_ah_zagros(roc_at_asset_hub_zagros.clone(), &receiver);
|
||||
|
||||
// Send TYRs over bridge
|
||||
{
|
||||
let destination = asset_hub_zagros_location();
|
||||
let assets: Assets = (roc_at_pezkuwichain_teyrchains.clone(), amount).into();
|
||||
let asset_transfer_type = TransferType::RemoteReserve(local_asset_hub.clone().into());
|
||||
let fees_id: AssetId = roc_at_pezkuwichain_teyrchains.clone().into();
|
||||
let fees_transfer_type = TransferType::RemoteReserve(local_asset_hub.into());
|
||||
let beneficiary: Location =
|
||||
AccountId32Junction { network: None, id: receiver.clone().into() }.into();
|
||||
let custom_xcm_on_dest = Xcm::<()>(vec![DepositAsset {
|
||||
assets: Wild(AllCounted(assets.len() as u32)),
|
||||
beneficiary,
|
||||
}]);
|
||||
send_assets_from_penpal_pezkuwichain_through_pezkuwichain_ah_to_zagros_ah(
|
||||
destination,
|
||||
(assets, asset_transfer_type),
|
||||
(fees_id, fees_transfer_type),
|
||||
custom_xcm_on_dest,
|
||||
);
|
||||
}
|
||||
|
||||
// process AHW incoming message and check events
|
||||
let roc = Location::new(2, [GlobalConsensus(ByGenesis(PEZKUWICHAIN_GENESIS_HASH))]);
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
// issue TYRs on AHW
|
||||
RuntimeEvent::ForeignAssets(pezpallet_assets::Event::Issued { asset_id, owner, .. }) => {
|
||||
asset_id: *asset_id == roc,
|
||||
owner: owner == &receiver,
|
||||
},
|
||||
// message processed successfully
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
let sender_rocs_after = PenpalA::execute_with(|| {
|
||||
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
|
||||
<ForeignAssets as Inspect<_>>::balance(roc_at_pezkuwichain_teyrchains, &sender)
|
||||
});
|
||||
let receiver_rocs_after = foreign_balance_on_ah_zagros(roc_at_asset_hub_zagros, &receiver);
|
||||
let rocs_in_reserve_on_ahr_after =
|
||||
<AssetHubPezkuwichain as Chain>::account_data_of(sov_ahw_on_ahr.clone()).free;
|
||||
|
||||
// Sender's balance is reduced
|
||||
assert!(sender_rocs_after < sender_rocs_before);
|
||||
// Receiver's balance is increased
|
||||
assert!(receiver_rocs_after > receiver_rocs_before);
|
||||
// Reserve balance is increased by sent amount (less fess)
|
||||
assert!(rocs_in_reserve_on_ahr_after > rocs_in_reserve_on_ahr_before);
|
||||
assert!(rocs_in_reserve_on_ahr_after <= rocs_in_reserve_on_ahr_before + amount);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_back_wnds_from_penpal_pezkuwichain_through_asset_hub_pezkuwichain_to_asset_hub_zagros() {
|
||||
let wnd_at_pezkuwichain_teyrchains = bridged_wnd_at_ah_pezkuwichain();
|
||||
let amount = ASSET_HUB_PEZKUWICHAIN_ED * 10_000_000;
|
||||
let sender = PenpalASender::get();
|
||||
let receiver = AssetHubZagrosReceiver::get();
|
||||
|
||||
// set up TYRs for transfer
|
||||
let (roc_at_pezkuwichain_teyrchains, _) =
|
||||
set_up_rocs_for_penpal_pezkuwichain_through_ahr_to_ahw(&sender, amount);
|
||||
|
||||
// set up ZGRs for transfer
|
||||
let penpal_location = AssetHubPezkuwichain::sibling_location_of(PenpalA::para_id());
|
||||
let sov_penpal_on_ahr = AssetHubPezkuwichain::sovereign_account_id_of(penpal_location);
|
||||
let prefund_accounts = vec![(sov_penpal_on_ahr, amount * 2)];
|
||||
let reserves = vec![(asset_hub_zagros_location(), false).into()];
|
||||
create_foreign_on_ah_pezkuwichain(
|
||||
wnd_at_pezkuwichain_teyrchains.clone(),
|
||||
true,
|
||||
reserves,
|
||||
prefund_accounts,
|
||||
);
|
||||
let asset_owner: AccountId = AssetHubPezkuwichain::account_id_of(ALICE);
|
||||
PenpalA::force_create_foreign_asset(
|
||||
wnd_at_pezkuwichain_teyrchains.clone(),
|
||||
asset_owner.clone(),
|
||||
true,
|
||||
ASSET_MIN_BALANCE,
|
||||
vec![(sender.clone(), amount * 2)],
|
||||
);
|
||||
// Configure source Penpal chain to trust local AH as reserve of bridged ZGR
|
||||
PenpalA::execute_with(|| {
|
||||
assert_ok!(<PenpalA as Chain>::System::set_storage(
|
||||
<PenpalA as Chain>::RuntimeOrigin::root(),
|
||||
vec![(
|
||||
PenpalCustomizableAssetFromSystemAssetHub::key().to_vec(),
|
||||
wnd_at_pezkuwichain_teyrchains.encode(),
|
||||
)],
|
||||
));
|
||||
});
|
||||
|
||||
// fund the AHR's SA on AHW with the ZGR tokens held in reserve
|
||||
let sov_ahr_on_ahw = AssetHubZagros::sovereign_account_of_teyrchain_on_other_global_consensus(
|
||||
NetworkId::ByGenesis(PEZKUWICHAIN_GENESIS_HASH),
|
||||
AssetHubPezkuwichain::para_id(),
|
||||
);
|
||||
AssetHubZagros::fund_accounts(vec![(sov_ahr_on_ahw.clone(), amount * 2)]);
|
||||
|
||||
// balances before
|
||||
let sender_wnds_before = PenpalA::execute_with(|| {
|
||||
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
|
||||
<ForeignAssets as Inspect<_>>::balance(
|
||||
wnd_at_pezkuwichain_teyrchains.clone().into(),
|
||||
&sender,
|
||||
)
|
||||
});
|
||||
let receiver_wnds_before = <AssetHubZagros as Chain>::account_data_of(receiver.clone()).free;
|
||||
|
||||
// send ZGRs over the bridge, TYRs only used to pay fees on local AH, pay with ZGR on remote AH
|
||||
{
|
||||
let final_destination = asset_hub_zagros_location();
|
||||
let intermediary_hop = PenpalA::sibling_location_of(AssetHubPezkuwichain::para_id());
|
||||
let context = PenpalA::execute_with(|| PenpalUniversalLocation::get());
|
||||
|
||||
// what happens at final destination
|
||||
let beneficiary = AccountId32Junction { network: None, id: receiver.clone().into() }.into();
|
||||
// use ZGR as fees on the final destination (AHW)
|
||||
let remote_fees: Asset = (wnd_at_pezkuwichain_teyrchains.clone(), amount).into();
|
||||
let remote_fees = remote_fees.reanchored(&final_destination, &context).unwrap();
|
||||
// buy execution using WNDs, then deposit all remaining WNDs
|
||||
let xcm_on_final_dest = Xcm::<()>(vec![
|
||||
BuyExecution { fees: remote_fees, weight_limit: WeightLimit::Unlimited },
|
||||
DepositAsset { assets: Wild(AllCounted(1)), beneficiary },
|
||||
]);
|
||||
|
||||
// what happens at intermediary hop
|
||||
// reanchor final dest (Asset Hub Zagros) to the view of hop (Asset Hub Pezkuwichain)
|
||||
let mut final_destination = final_destination.clone();
|
||||
final_destination.reanchor(&intermediary_hop, &context).unwrap();
|
||||
// reanchor ZGRs to the view of hop (Asset Hub Pezkuwichain)
|
||||
let asset: Asset = (wnd_at_pezkuwichain_teyrchains.clone(), amount).into();
|
||||
let asset = asset.reanchored(&intermediary_hop, &context).unwrap();
|
||||
// on Asset Hub Pezkuwichain, forward a request to withdraw ZGRs from reserve on Asset Hub
|
||||
// Zagros
|
||||
let xcm_on_hop = Xcm::<()>(vec![InitiateReserveWithdraw {
|
||||
assets: Definite(asset.into()), // WNDs
|
||||
reserve: final_destination, // AHW
|
||||
xcm: xcm_on_final_dest, // XCM to execute on AHW
|
||||
}]);
|
||||
// assets to send from Penpal and how they reach the intermediary hop
|
||||
let assets: Assets = vec![
|
||||
(wnd_at_pezkuwichain_teyrchains.clone(), amount).into(),
|
||||
(roc_at_pezkuwichain_teyrchains.clone(), amount).into(),
|
||||
]
|
||||
.into();
|
||||
let asset_transfer_type = TransferType::DestinationReserve;
|
||||
let fees_id: AssetId = roc_at_pezkuwichain_teyrchains.into();
|
||||
let fees_transfer_type = TransferType::DestinationReserve;
|
||||
|
||||
// initiate the transfer
|
||||
send_assets_from_penpal_pezkuwichain_through_pezkuwichain_ah_to_zagros_ah(
|
||||
intermediary_hop,
|
||||
(assets, asset_transfer_type),
|
||||
(fees_id, fees_transfer_type),
|
||||
xcm_on_hop,
|
||||
);
|
||||
}
|
||||
|
||||
// process AHW incoming message and check events
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
// issue TYRs on AHW
|
||||
RuntimeEvent::Balances(pezpallet_balances::Event::Issued { .. }) => {},
|
||||
// message processed successfully
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
let sender_wnds_after = PenpalA::execute_with(|| {
|
||||
type ForeignAssets = <PenpalA as PenpalAPallet>::ForeignAssets;
|
||||
<ForeignAssets as Inspect<_>>::balance(wnd_at_pezkuwichain_teyrchains.into(), &sender)
|
||||
});
|
||||
let receiver_wnds_after = <AssetHubZagros as Chain>::account_data_of(receiver).free;
|
||||
|
||||
// Sender's balance is reduced by sent "amount"
|
||||
assert_eq!(sender_wnds_after, sender_wnds_before - amount);
|
||||
// Receiver's balance is increased by no more than "amount"
|
||||
assert!(receiver_wnds_after > receiver_wnds_before);
|
||||
assert!(receiver_wnds_after <= receiver_wnds_before + amount);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dry_run_transfer_to_zagros_sends_xcm_to_bridge_hub() {
|
||||
test_dry_run_transfer_across_pk_bridge!(
|
||||
AssetHubPezkuwichain,
|
||||
BridgeHubPezkuwichain,
|
||||
asset_hub_zagros_location()
|
||||
);
|
||||
}
|
||||
+34
@@ -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 = BridgeHubPezkuwichainExistentialDeposit::get();
|
||||
let assets: Assets = (Parent, amount).into();
|
||||
|
||||
test_chain_can_claim_assets!(
|
||||
AssetHubPezkuwichain,
|
||||
RuntimeCall,
|
||||
NetworkId::ByGenesis(PEZKUWICHAIN_GENESIS_HASH),
|
||||
assets,
|
||||
amount
|
||||
);
|
||||
}
|
||||
+303
@@ -0,0 +1,303 @@
|
||||
// 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::{snowbridge, snowbridge::WETH};
|
||||
use testnet_teyrchains_constants::pezkuwichain::snowbridge::EthereumNetwork;
|
||||
use xcm::opaque::v5;
|
||||
use xcm_executor::traits::ConvertLocation;
|
||||
|
||||
mod asset_transfers;
|
||||
mod claim_assets;
|
||||
mod register_bridged_assets;
|
||||
mod send_xcm;
|
||||
mod teleport;
|
||||
|
||||
pub(crate) fn asset_hub_zagros_location() -> Location {
|
||||
Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
Teyrchain(AssetHubZagros::para_id().into()),
|
||||
],
|
||||
)
|
||||
}
|
||||
pub(crate) fn asset_hub_pezkuwichain_global_location() -> Location {
|
||||
Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(ByGenesis(PEZKUWICHAIN_GENESIS_HASH)),
|
||||
Teyrchain(AssetHubPezkuwichain::para_id().into()),
|
||||
],
|
||||
)
|
||||
}
|
||||
pub(crate) fn bridge_hub_zagros_location() -> Location {
|
||||
Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
Teyrchain(BridgeHubZagros::para_id().into()),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
// TYR and wTYR
|
||||
pub(crate) fn roc_at_ah_pezkuwichain() -> Location {
|
||||
Parent.into()
|
||||
}
|
||||
pub(crate) fn bridged_roc_at_ah_zagros() -> Location {
|
||||
Location::new(2, [GlobalConsensus(ByGenesis(PEZKUWICHAIN_GENESIS_HASH))])
|
||||
}
|
||||
|
||||
// ZGR and wWND
|
||||
pub(crate) fn bridged_wnd_at_ah_pezkuwichain() -> Location {
|
||||
Location::new(2, [GlobalConsensus(ByGenesis(ZAGROS_GENESIS_HASH))])
|
||||
}
|
||||
|
||||
// USDT and wUSDT
|
||||
pub(crate) fn usdt_at_ah_zagros() -> Location {
|
||||
Location::new(0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(USDT_ID.into())])
|
||||
}
|
||||
pub(crate) fn bridged_usdt_at_ah_pezkuwichain() -> Location {
|
||||
Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
Teyrchain(AssetHubZagros::para_id().into()),
|
||||
PalletInstance(ASSETS_PALLET_ID),
|
||||
GeneralIndex(USDT_ID.into()),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
// wETH has same relative location on both Pezkuwichain and Zagros AssetHubs
|
||||
pub(crate) fn weth_at_asset_hubs() -> Location {
|
||||
Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(Ethereum { chain_id: snowbridge::SEPOLIA_ID }),
|
||||
AccountKey20 { network: None, key: WETH },
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn create_foreign_on_ah_pezkuwichain(
|
||||
id: v5::Location,
|
||||
sufficient: bool,
|
||||
reserves: Vec<ForeignAssetReserveData>,
|
||||
prefund_accounts: Vec<(AccountId, u128)>,
|
||||
) {
|
||||
let owner = AssetHubPezkuwichain::account_id_of(ALICE);
|
||||
let min = ASSET_MIN_BALANCE;
|
||||
AssetHubPezkuwichain::force_create_foreign_asset(
|
||||
id.clone(),
|
||||
owner.clone(),
|
||||
sufficient,
|
||||
min,
|
||||
prefund_accounts,
|
||||
);
|
||||
AssetHubPezkuwichain::set_foreign_asset_reserves(id, owner, reserves);
|
||||
}
|
||||
|
||||
pub(crate) fn create_foreign_on_ah_zagros(
|
||||
id: v5::Location,
|
||||
sufficient: bool,
|
||||
reserves: Vec<ForeignAssetReserveData>,
|
||||
) {
|
||||
let owner = AssetHubZagros::account_id_of(ALICE);
|
||||
AssetHubZagros::force_create_foreign_asset(
|
||||
id.clone(),
|
||||
owner.clone(),
|
||||
sufficient,
|
||||
ASSET_MIN_BALANCE,
|
||||
vec![],
|
||||
);
|
||||
AssetHubZagros::set_foreign_asset_reserves(id, owner, reserves);
|
||||
}
|
||||
|
||||
pub(crate) fn foreign_balance_on_ah_pezkuwichain(id: v5::Location, who: &AccountId) -> u128 {
|
||||
AssetHubPezkuwichain::execute_with(|| {
|
||||
type Assets = <AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::ForeignAssets;
|
||||
<Assets as Inspect<_>>::balance(id, who)
|
||||
})
|
||||
}
|
||||
pub(crate) fn foreign_balance_on_ah_zagros(id: v5::Location, who: &AccountId) -> u128 {
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type Assets = <AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets;
|
||||
<Assets as Inspect<_>>::balance(id, who)
|
||||
})
|
||||
}
|
||||
|
||||
// set up pool
|
||||
pub(crate) fn set_up_pool_with_wnd_on_ah_zagros(asset: v5::Location, is_foreign: bool) {
|
||||
let wnd: v5::Location = v5::Parent.into();
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
let owner = AssetHubZagrosSender::get();
|
||||
let signed_owner = <AssetHubZagros as Chain>::RuntimeOrigin::signed(owner.clone());
|
||||
|
||||
if is_foreign {
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::mint(
|
||||
signed_owner.clone(),
|
||||
asset.clone().into(),
|
||||
owner.clone().into(),
|
||||
3_000_000_000_000,
|
||||
));
|
||||
} else {
|
||||
let asset_id = match asset.interior.last() {
|
||||
Some(GeneralIndex(id)) => *id as u32,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::Assets::mint(
|
||||
signed_owner.clone(),
|
||||
asset_id.into(),
|
||||
owner.clone().into(),
|
||||
3_000_000_000_000,
|
||||
));
|
||||
}
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::AssetConversion::create_pool(
|
||||
signed_owner.clone(),
|
||||
Box::new(wnd.clone()),
|
||||
Box::new(asset.clone()),
|
||||
));
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::AssetConversion(pezpallet_asset_conversion::Event::PoolCreated { .. }) => {},
|
||||
]
|
||||
);
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::AssetConversion::add_liquidity(
|
||||
signed_owner.clone(),
|
||||
Box::new(wnd),
|
||||
Box::new(asset),
|
||||
1_000_000_000_000,
|
||||
2_000_000_000_000,
|
||||
1,
|
||||
1,
|
||||
owner.into()
|
||||
));
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::AssetConversion(pezpallet_asset_conversion::Event::LiquidityAdded {..}) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn send_assets_from_asset_hub_pezkuwichain(
|
||||
destination: Location,
|
||||
assets: Assets,
|
||||
fee_idx: u32,
|
||||
// For knowing what reserve to pick.
|
||||
// We only allow using the same transfer type for assets and fees right now.
|
||||
// And only `LocalReserve` or `DestinationReserve`.
|
||||
transfer_type: TransferType,
|
||||
) -> DispatchResult {
|
||||
let signed_origin =
|
||||
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(AssetHubPezkuwichainSender::get());
|
||||
let beneficiary: Location =
|
||||
AccountId32Junction { network: None, id: AssetHubZagrosReceiver::get().into() }.into();
|
||||
|
||||
type Runtime = <AssetHubPezkuwichain as Chain>::Runtime;
|
||||
let remote_fee_id: AssetId = assets
|
||||
.clone()
|
||||
.into_inner()
|
||||
.get(fee_idx as usize)
|
||||
.ok_or(pezpallet_xcm::Error::<Runtime>::Empty)?
|
||||
.clone()
|
||||
.id;
|
||||
|
||||
AssetHubPezkuwichain::execute_with(|| {
|
||||
<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::PezkuwiXcm::transfer_assets_using_type_and_then(
|
||||
signed_origin,
|
||||
bx!(destination.into()),
|
||||
bx!(assets.into()),
|
||||
bx!(transfer_type.clone()),
|
||||
bx!(remote_fee_id.into()),
|
||||
bx!(transfer_type),
|
||||
bx!(VersionedXcm::from(
|
||||
Xcm::<()>::builder_unsafe().deposit_asset(AllCounted(1), beneficiary).build()
|
||||
)),
|
||||
WeightLimit::Unlimited,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn assert_bridge_hub_pezkuwichain_message_accepted(expected_processed: bool) {
|
||||
BridgeHubPezkuwichain::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubPezkuwichain as Chain>::RuntimeEvent;
|
||||
|
||||
if expected_processed {
|
||||
assert_expected_events!(
|
||||
BridgeHubPezkuwichain,
|
||||
vec![
|
||||
// pay for bridge fees
|
||||
RuntimeEvent::Balances(pezpallet_balances::Event::Burned { .. }) => {},
|
||||
// message exported
|
||||
RuntimeEvent::BridgeZagrosMessages(
|
||||
pezpallet_bridge_messages::Event::MessageAccepted { .. }
|
||||
) => {},
|
||||
// message processed successfully
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
} else {
|
||||
assert_expected_events!(
|
||||
BridgeHubPezkuwichain,
|
||||
vec![
|
||||
RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed {
|
||||
success: false,
|
||||
..
|
||||
}) => {},
|
||||
]
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn assert_bridge_hub_zagros_message_received() {
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![
|
||||
// message sent to destination
|
||||
RuntimeEvent::XcmpQueue(
|
||||
cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
pub fn snowbridge_sovereign() -> pezsp_runtime::AccountId32 {
|
||||
use asset_hub_pezkuwichain_runtime::xcm_config::UniversalLocation as AssetHubPezkuwichainUniversalLocation;
|
||||
let ethereum_sovereign: AccountId = AssetHubPezkuwichain::execute_with(|| {
|
||||
ExternalConsensusLocationsConverterFor::<
|
||||
AssetHubPezkuwichainUniversalLocation,
|
||||
[u8; 32],
|
||||
>::convert_location(&Location::new(
|
||||
2,
|
||||
[xcm::v5::Junction::GlobalConsensus(EthereumNetwork::get())],
|
||||
))
|
||||
.unwrap()
|
||||
.into()
|
||||
});
|
||||
|
||||
ethereum_sovereign
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
// 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::*, tests::*};
|
||||
|
||||
const XCM_FEE: u128 = 4_000_000_000_000;
|
||||
|
||||
/// Tests the registering of a Pezkuwichain Asset as a bridged asset on Zagros Asset Hub.
|
||||
#[test]
|
||||
fn register_pezkuwichain_asset_on_wah_from_rah() {
|
||||
let sa_of_rah_on_wah = AssetHubZagros::sovereign_account_of_teyrchain_on_other_global_consensus(
|
||||
ByGenesis(PEZKUWICHAIN_GENESIS_HASH),
|
||||
AssetHubPezkuwichain::para_id(),
|
||||
);
|
||||
|
||||
// Pezkuwichain Asset Hub asset when bridged to Zagros Asset Hub.
|
||||
let bridged_asset_at_wah = Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(ByGenesis(PEZKUWICHAIN_GENESIS_HASH)),
|
||||
Teyrchain(AssetHubPezkuwichain::para_id().into()),
|
||||
PalletInstance(ASSETS_PALLET_ID),
|
||||
GeneralIndex(ASSET_ID.into()),
|
||||
],
|
||||
);
|
||||
|
||||
// Encoded `create_asset` call to be executed in Zagros Asset Hub ForeignAssets pallet.
|
||||
let call = AssetHubZagros::create_foreign_asset_call(
|
||||
bridged_asset_at_wah.clone(),
|
||||
ASSET_MIN_BALANCE,
|
||||
sa_of_rah_on_wah.clone(),
|
||||
);
|
||||
|
||||
let origin_kind = OriginKind::Xcm;
|
||||
let fee_amount = XCM_FEE;
|
||||
let fees = (Parent, fee_amount).into();
|
||||
|
||||
let xcm = xcm_transact_paid_execution(call, origin_kind, fees, sa_of_rah_on_wah.clone());
|
||||
|
||||
// SA-of-RAH-on-WAH needs to have balance to pay for fees and asset creation deposit
|
||||
AssetHubZagros::fund_accounts(vec![(
|
||||
sa_of_rah_on_wah.clone(),
|
||||
ASSET_HUB_ZAGROS_ED * 10000000000,
|
||||
)]);
|
||||
|
||||
let destination = asset_hub_zagros_location();
|
||||
|
||||
// fund the RAH's SA on RBH for paying bridge delivery fees
|
||||
BridgeHubPezkuwichain::fund_para_sovereign(
|
||||
AssetHubPezkuwichain::para_id(),
|
||||
10_000_000_000_000u128,
|
||||
);
|
||||
|
||||
// set XCM versions
|
||||
AssetHubPezkuwichain::force_xcm_version(destination.clone(), XCM_VERSION);
|
||||
BridgeHubPezkuwichain::force_xcm_version(bridge_hub_zagros_location(), XCM_VERSION);
|
||||
|
||||
let root_origin = <AssetHubPezkuwichain as Chain>::RuntimeOrigin::root();
|
||||
AssetHubPezkuwichain::execute_with(|| {
|
||||
assert_ok!(<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::PezkuwiXcm::send(
|
||||
root_origin,
|
||||
bx!(destination.into()),
|
||||
bx!(xcm),
|
||||
));
|
||||
|
||||
AssetHubPezkuwichain::assert_xcm_pallet_sent();
|
||||
});
|
||||
|
||||
assert_bridge_hub_pezkuwichain_message_accepted(true);
|
||||
assert_bridge_hub_zagros_message_received();
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
AssetHubZagros::assert_xcmp_queue_success(None);
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
// Burned the fee
|
||||
RuntimeEvent::Balances(pezpallet_balances::Event::Burned { who, amount }) => {
|
||||
who: *who == sa_of_rah_on_wah.clone(),
|
||||
amount: *amount == fee_amount,
|
||||
},
|
||||
// Foreign Asset created
|
||||
RuntimeEvent::ForeignAssets(pezpallet_assets::Event::Created { asset_id, creator, owner }) => {
|
||||
asset_id: asset_id == &bridged_asset_at_wah,
|
||||
creator: *creator == sa_of_rah_on_wah.clone(),
|
||||
owner: *owner == sa_of_rah_on_wah,
|
||||
},
|
||||
// Unspent fee minted to origin
|
||||
RuntimeEvent::Balances(pezpallet_balances::Event::Minted { who, .. }) => {
|
||||
who: *who == sa_of_rah_on_wah.clone(),
|
||||
},
|
||||
]
|
||||
);
|
||||
type ForeignAssets = <AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets;
|
||||
assert!(ForeignAssets::asset_exists(bridged_asset_at_wah));
|
||||
});
|
||||
}
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
// 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 pezkuwichain_system_emulated_network::pezkuwichain_emulated_chain::pezkuwichain_runtime::Dmp;
|
||||
|
||||
use crate::tests::*;
|
||||
|
||||
#[test]
|
||||
fn send_xcm_from_pezkuwichain_relay_to_zagros_asset_hub_should_fail_on_not_applicable() {
|
||||
// Init tests variables
|
||||
// XcmPallet send arguments
|
||||
let sudo_origin = <Pezkuwichain as Chain>::RuntimeOrigin::root();
|
||||
let destination = Pezkuwichain::child_location_of(BridgeHubPezkuwichain::para_id()).into();
|
||||
let weight_limit = WeightLimit::Unlimited;
|
||||
let check_origin = None;
|
||||
|
||||
let remote_xcm = Xcm(vec![ClearOrigin]);
|
||||
|
||||
let xcm = VersionedXcm::from(Xcm(vec![
|
||||
UnpaidExecution { weight_limit, check_origin },
|
||||
ExportMessage {
|
||||
network: ByGenesis(ZAGROS_GENESIS_HASH),
|
||||
destination: [Teyrchain(AssetHubZagros::para_id().into())].into(),
|
||||
xcm: remote_xcm,
|
||||
},
|
||||
]));
|
||||
|
||||
// Pezkuwichain Global Consensus
|
||||
// Send XCM message from Relay Chain to Bridge Hub source Teyrchain
|
||||
Pezkuwichain::execute_with(|| {
|
||||
Dmp::make_teyrchain_reachable(BridgeHubPezkuwichain::para_id());
|
||||
|
||||
assert_ok!(<Pezkuwichain as PezkuwichainPallet>::XcmPallet::send(
|
||||
sudo_origin,
|
||||
bx!(destination),
|
||||
bx!(xcm),
|
||||
));
|
||||
|
||||
type RuntimeEvent = <Pezkuwichain as Chain>::RuntimeEvent;
|
||||
|
||||
assert_expected_events!(
|
||||
Pezkuwichain,
|
||||
vec![
|
||||
RuntimeEvent::XcmPallet(pezpallet_xcm::Event::Sent { .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
// Receive XCM message in Bridge Hub source Teyrchain, it should fail, because we don't have
|
||||
// opened bridge/lane.
|
||||
assert_bridge_hub_pezkuwichain_message_accepted(false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_xcm_through_opened_lane_with_different_xcm_version_on_hops_works() {
|
||||
// prepare data
|
||||
let destination = asset_hub_zagros_location();
|
||||
let native_token = Location::parent();
|
||||
let amount = ASSET_HUB_PEZKUWICHAIN_ED * 1_000;
|
||||
|
||||
// fund the AHR's SA on BHR for paying bridge delivery fees
|
||||
BridgeHubPezkuwichain::fund_para_sovereign(
|
||||
AssetHubPezkuwichain::para_id(),
|
||||
10_000_000_000_000u128,
|
||||
);
|
||||
// fund sender
|
||||
AssetHubPezkuwichain::fund_accounts(vec![(
|
||||
AssetHubPezkuwichainSender::get().into(),
|
||||
amount * 10,
|
||||
)]);
|
||||
|
||||
// Initially set only default version on all runtimes
|
||||
let newer_xcm_version = xcm::prelude::XCM_VERSION;
|
||||
let older_xcm_version = newer_xcm_version - 1;
|
||||
AssetHubPezkuwichain::force_default_xcm_version(Some(older_xcm_version));
|
||||
BridgeHubPezkuwichain::force_default_xcm_version(Some(older_xcm_version));
|
||||
BridgeHubZagros::force_default_xcm_version(Some(older_xcm_version));
|
||||
AssetHubZagros::force_default_xcm_version(Some(older_xcm_version));
|
||||
|
||||
// send XCM from AssetHubPezkuwichain - fails - destination version not known
|
||||
assert_err!(
|
||||
send_assets_from_asset_hub_pezkuwichain(
|
||||
destination.clone(),
|
||||
(native_token.clone(), amount).into(),
|
||||
0,
|
||||
TransferType::LocalReserve
|
||||
),
|
||||
DispatchError::Module(pezsp_runtime::ModuleError {
|
||||
index: 31,
|
||||
error: [1, 0, 0, 0],
|
||||
message: Some("SendFailure")
|
||||
})
|
||||
);
|
||||
|
||||
// set destination version
|
||||
AssetHubPezkuwichain::force_xcm_version(destination.clone(), newer_xcm_version);
|
||||
|
||||
// set version with `ExportMessage` for BridgeHubPezkuwichain
|
||||
AssetHubPezkuwichain::force_xcm_version(
|
||||
ParentThen(Teyrchain(BridgeHubPezkuwichain::para_id().into()).into()).into(),
|
||||
newer_xcm_version,
|
||||
);
|
||||
// send XCM from AssetHubPezkuwichain - ok
|
||||
assert_ok!(send_assets_from_asset_hub_pezkuwichain(
|
||||
destination.clone(),
|
||||
(native_token.clone(), amount).into(),
|
||||
0,
|
||||
TransferType::LocalReserve
|
||||
));
|
||||
|
||||
// `ExportMessage` on local BridgeHub - fails - remote BridgeHub version not known
|
||||
assert_bridge_hub_pezkuwichain_message_accepted(false);
|
||||
|
||||
// set version for remote BridgeHub on BridgeHubPezkuwichain
|
||||
BridgeHubPezkuwichain::force_xcm_version(bridge_hub_zagros_location(), newer_xcm_version);
|
||||
// set version for AssetHubZagros on BridgeHubZagros
|
||||
BridgeHubZagros::force_xcm_version(
|
||||
ParentThen(Teyrchain(AssetHubZagros::para_id().into()).into()).into(),
|
||||
newer_xcm_version,
|
||||
);
|
||||
|
||||
// send XCM from AssetHubPezkuwichain - ok
|
||||
assert_ok!(send_assets_from_asset_hub_pezkuwichain(
|
||||
destination.clone(),
|
||||
(native_token.clone(), amount).into(),
|
||||
0,
|
||||
TransferType::LocalReserve
|
||||
));
|
||||
assert_bridge_hub_pezkuwichain_message_accepted(true);
|
||||
assert_bridge_hub_zagros_message_received();
|
||||
// message delivered and processed at destination
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
// message processed with failure, but for this scenario it is ok, important is that was delivered
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: false, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
// 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 teleport_via_limited_teleport_assets_to_other_system_teyrchains_works() {
|
||||
let amount = BRIDGE_HUB_PEZKUWICHAIN_ED * 100;
|
||||
let native_asset: Assets = (Parent, amount).into();
|
||||
|
||||
let fee_asset_id: AssetId = Parent.into();
|
||||
test_teyrchain_is_trusted_teleporter!(
|
||||
BridgeHubPezkuwichain, // Origin
|
||||
vec![AssetHubPezkuwichain], // Destinations
|
||||
(native_asset, amount),
|
||||
fee_asset_id,
|
||||
limited_teleport_assets
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleport_via_transfer_assets_to_other_system_teyrchains_works() {
|
||||
let amount = BRIDGE_HUB_PEZKUWICHAIN_ED * 100;
|
||||
let native_asset: Assets = (Parent, amount).into();
|
||||
|
||||
let fee_asset_id: AssetId = Parent.into();
|
||||
test_teyrchain_is_trusted_teleporter!(
|
||||
BridgeHubPezkuwichain, // Origin
|
||||
vec![AssetHubPezkuwichain], // 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![BridgeHubPezkuwichain],
|
||||
amount,
|
||||
limited_teleport_assets
|
||||
);
|
||||
|
||||
test_teyrchain_is_trusted_teleporter_for_relay!(
|
||||
BridgeHubPezkuwichain,
|
||||
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![BridgeHubPezkuwichain],
|
||||
amount,
|
||||
transfer_assets
|
||||
);
|
||||
|
||||
test_teyrchain_is_trusted_teleporter_for_relay!(
|
||||
BridgeHubPezkuwichain,
|
||||
Pezkuwichain,
|
||||
amount,
|
||||
transfer_assets
|
||||
);
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
[package]
|
||||
name = "bridge-hub-zagros-integration-tests"
|
||||
version = "1.0.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license = "Apache-2.0"
|
||||
description = "Bridge Hub Zagros runtime integration tests with xcm-emulator"
|
||||
publish = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
codec = { workspace = true }
|
||||
hex-literal = { workspace = true, default-features = true }
|
||||
scale-info = { workspace = true }
|
||||
|
||||
# Bizinikiwi
|
||||
pezframe-support = { workspace = true }
|
||||
pezpallet-asset-conversion = { workspace = true }
|
||||
pezpallet-assets = { workspace = true }
|
||||
pezpallet-balances = { workspace = true }
|
||||
pezpallet-message-queue = { workspace = true, default-features = true }
|
||||
pezsp-core = { workspace = true }
|
||||
pezsp-io = { workspace = true }
|
||||
pezsp-runtime = { workspace = true }
|
||||
|
||||
# Pezkuwi
|
||||
pezpallet-xcm = { workspace = true }
|
||||
xcm = { workspace = true }
|
||||
xcm-builder = { workspace = true }
|
||||
xcm-executor = { workspace = true }
|
||||
|
||||
# Bridges
|
||||
pezpallet-bridge-messages = { workspace = true }
|
||||
pezpallet-bridge-relayers = { workspace = true }
|
||||
|
||||
# Pezcumulus
|
||||
asset-hub-zagros-runtime = { workspace = true }
|
||||
bp-asset-hub-zagros = { workspace = true }
|
||||
bridge-hub-common = { workspace = true }
|
||||
bridge-hub-zagros-runtime = { workspace = true }
|
||||
pezcumulus-pezpallet-teyrchain-system = { workspace = true }
|
||||
pezcumulus-pezpallet-xcmp-queue = { workspace = true }
|
||||
emulated-integration-tests-common = { workspace = true }
|
||||
pezkuwichain-zagros-system-emulated-network = { workspace = true }
|
||||
testnet-teyrchains-constants = { features = [
|
||||
"pezkuwichain",
|
||||
"zagros",
|
||||
], workspace = true, default-features = true }
|
||||
teyrchains-common = { workspace = true, default-features = true }
|
||||
|
||||
# Snowbridge
|
||||
snowbridge-core = { workspace = true }
|
||||
snowbridge-inbound-queue-primitives = { workspace = true }
|
||||
snowbridge-outbound-queue-primitives = { workspace = true }
|
||||
snowbridge-pezpallet-inbound-queue = { workspace = true }
|
||||
snowbridge-pezpallet-inbound-queue-fixtures = { workspace = true }
|
||||
snowbridge-pezpallet-inbound-queue-v2 = { workspace = true }
|
||||
snowbridge-pezpallet-outbound-queue = { workspace = true }
|
||||
snowbridge-pezpallet-outbound-queue-v2 = { workspace = true }
|
||||
snowbridge-pezpallet-system = { workspace = true }
|
||||
snowbridge-pezpallet-system-v2 = { workspace = true }
|
||||
|
||||
[features]
|
||||
runtime-benchmarks = [
|
||||
"asset-hub-zagros-runtime/runtime-benchmarks",
|
||||
"bp-asset-hub-zagros/runtime-benchmarks",
|
||||
"bridge-hub-common/runtime-benchmarks",
|
||||
"bridge-hub-zagros-runtime/runtime-benchmarks",
|
||||
"pezcumulus-pezpallet-teyrchain-system/runtime-benchmarks",
|
||||
"pezcumulus-pezpallet-xcmp-queue/runtime-benchmarks",
|
||||
"emulated-integration-tests-common/runtime-benchmarks",
|
||||
"pezframe-support/runtime-benchmarks",
|
||||
"pezpallet-asset-conversion/runtime-benchmarks",
|
||||
"pezpallet-assets/runtime-benchmarks",
|
||||
"pezpallet-balances/runtime-benchmarks",
|
||||
"pezpallet-bridge-messages/runtime-benchmarks",
|
||||
"pezpallet-bridge-relayers/runtime-benchmarks",
|
||||
"pezpallet-message-queue/runtime-benchmarks",
|
||||
"pezpallet-xcm/runtime-benchmarks",
|
||||
"pezkuwichain-zagros-system-emulated-network/runtime-benchmarks",
|
||||
"snowbridge-core/runtime-benchmarks",
|
||||
"snowbridge-inbound-queue-primitives/runtime-benchmarks",
|
||||
"snowbridge-outbound-queue-primitives/runtime-benchmarks",
|
||||
"snowbridge-pezpallet-inbound-queue-fixtures/runtime-benchmarks",
|
||||
"snowbridge-pezpallet-inbound-queue-v2/runtime-benchmarks",
|
||||
"snowbridge-pezpallet-inbound-queue/runtime-benchmarks",
|
||||
"snowbridge-pezpallet-outbound-queue-v2/runtime-benchmarks",
|
||||
"snowbridge-pezpallet-outbound-queue/runtime-benchmarks",
|
||||
"snowbridge-pezpallet-system-v2/runtime-benchmarks",
|
||||
"snowbridge-pezpallet-system/runtime-benchmarks",
|
||||
"pezsp-io/runtime-benchmarks",
|
||||
"pezsp-runtime/runtime-benchmarks",
|
||||
"testnet-teyrchains-constants/runtime-benchmarks",
|
||||
"teyrchains-common/runtime-benchmarks",
|
||||
"xcm-builder/runtime-benchmarks",
|
||||
"xcm-executor/runtime-benchmarks",
|
||||
"xcm/runtime-benchmarks",
|
||||
]
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
// 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 {
|
||||
// Bizinikiwi
|
||||
pub(crate) use codec::Encode;
|
||||
pub(crate) use pezframe_support::{
|
||||
assert_err, assert_ok, pezpallet_prelude::DispatchResult, BoundedVec,
|
||||
};
|
||||
pub(crate) use pezsp_core::H160;
|
||||
pub(crate) use pezsp_runtime::DispatchError;
|
||||
|
||||
// Pezkuwi
|
||||
pub(crate) use xcm::{
|
||||
latest::{ParentThen, PEZKUWICHAIN_GENESIS_HASH, ZAGROS_GENESIS_HASH},
|
||||
prelude::{AccountId32 as AccountId32Junction, *},
|
||||
v5,
|
||||
};
|
||||
pub(crate) use xcm_executor::traits::TransferType;
|
||||
// Pezcumulus
|
||||
pub(crate) use emulated_integration_tests_common::{
|
||||
accounts::ALICE,
|
||||
create_pool_with_native_on,
|
||||
impls::Inspect,
|
||||
test_dry_run_transfer_across_pk_bridge, test_relay_is_trusted_teleporter,
|
||||
test_teyrchain_is_trusted_teleporter, test_teyrchain_is_trusted_teleporter_for_relay,
|
||||
xcm_emulator::{
|
||||
assert_expected_events, bx, Chain, RelayChain as Relay, TestExt, Teyrchain as Para,
|
||||
},
|
||||
xcm_helpers::xcm_transact_paid_execution,
|
||||
ASSETS_PALLET_ID, USDT_ID,
|
||||
};
|
||||
pub(crate) use pezkuwichain_zagros_system_emulated_network::{
|
||||
asset_hub_pezkuwichain_emulated_chain::{
|
||||
asset_hub_pezkuwichain_runtime::{
|
||||
xcm_config::TreasuryAccount, ForeignAssetReserveData,
|
||||
},
|
||||
genesis::ED as ASSET_HUB_PEZKUWICHAIN_ED,
|
||||
AssetHubPezkuwichainParaPallet as AssetHubPezkuwichainPallet,
|
||||
},
|
||||
asset_hub_zagros_emulated_chain::{
|
||||
genesis::{AssetHubZagrosAssetOwner, ED as ASSET_HUB_ZAGROS_ED},
|
||||
AssetHubZagrosParaPallet as AssetHubZagrosPallet,
|
||||
},
|
||||
bridge_hub_zagros_emulated_chain::{
|
||||
bridge_hub_zagros_runtime, genesis::ED as BRIDGE_HUB_ZAGROS_ED,
|
||||
BridgeHubZagrosExistentialDeposit, BridgeHubZagrosParaPallet as BridgeHubZagrosPallet,
|
||||
BridgeHubZagrosRuntimeOrigin,
|
||||
},
|
||||
penpal_emulated_chain::{
|
||||
self,
|
||||
penpal_runtime::xcm_config::{
|
||||
CustomizableAssetFromSystemAssetHub as PenpalCustomizableAssetFromSystemAssetHub,
|
||||
LocalTeleportableToAssetHub as PenpalLocalTeleportableToAssetHub,
|
||||
UniversalLocation as PenpalUniversalLocation,
|
||||
},
|
||||
PenpalAParaPallet as PenpalAPallet, PenpalAssetOwner,
|
||||
PenpalBParaPallet as PenpalBPallet,
|
||||
},
|
||||
pezkuwichain_emulated_chain::PezkuwichainRelayPallet as PezkuwichainPallet,
|
||||
zagros_emulated_chain::{genesis::ED as ZAGROS_ED, ZagrosRelayPallet as ZagrosPallet},
|
||||
AssetHubPezkuwichainPara as AssetHubPezkuwichain,
|
||||
AssetHubPezkuwichainParaReceiver as AssetHubPezkuwichainReceiver,
|
||||
AssetHubPezkuwichainParaSender as AssetHubPezkuwichainSender,
|
||||
AssetHubZagrosPara as AssetHubZagros, AssetHubZagrosParaReceiver as AssetHubZagrosReceiver,
|
||||
AssetHubZagrosParaSender as AssetHubZagrosSender,
|
||||
BridgeHubPezkuwichainPara as BridgeHubPezkuwichain, BridgeHubZagrosPara as BridgeHubZagros,
|
||||
BridgeHubZagrosParaReceiver as BridgeHubZagrosReceiver,
|
||||
BridgeHubZagrosParaSender as BridgeHubZagrosSender, PenpalAPara as PenpalA,
|
||||
PenpalAParaReceiver as PenpalAReceiver, PenpalBPara as PenpalB,
|
||||
PenpalBParaReceiver as PenpalBReceiver, PenpalBParaSender as PenpalBSender,
|
||||
PezkuwichainRelay as Pezkuwichain, PezkuwichainRelayReceiver as PezkuwichainReceiver,
|
||||
ZagrosRelay as Zagros, ZagrosRelayReceiver as ZagrosReceiver,
|
||||
ZagrosRelaySender as ZagrosSender,
|
||||
};
|
||||
pub(crate) use teyrchains_common::AccountId;
|
||||
|
||||
pub(crate) const ASSET_ID: u32 = 1;
|
||||
pub(crate) const ASSET_MIN_BALANCE: u128 = 1000;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
+254
@@ -0,0 +1,254 @@
|
||||
// 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 XCM aliasing.
|
||||
|
||||
use crate::imports::*;
|
||||
use bridge_hub_zagros_runtime::xcm_config::XcmConfig;
|
||||
use emulated_integration_tests_common::{macros::AccountId, test_cross_chain_alias};
|
||||
use pezframe_support::traits::ContainsPair;
|
||||
use xcm::latest::Junctions::*;
|
||||
|
||||
const ALLOWED: bool = true;
|
||||
const DENIED: bool = false;
|
||||
|
||||
const TELEPORT_FEES: bool = true;
|
||||
const RESERVE_TRANSFER_FEES: bool = false;
|
||||
|
||||
#[test]
|
||||
fn account_on_sibling_syschain_aliases_into_same_local_account() {
|
||||
// origin and target are the same account on different chains
|
||||
let origin: AccountId = [1; 32].into();
|
||||
let target = origin.clone();
|
||||
let fees = ZAGROS_ED * 10;
|
||||
|
||||
PenpalB::mint_foreign_asset(
|
||||
<PenpalB as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get()),
|
||||
Location::parent(),
|
||||
origin.clone(),
|
||||
fees * 10,
|
||||
);
|
||||
|
||||
// On Bridge Hub we don't want to support aliasing from other chains: there is no real world
|
||||
// demand for it, only low-level power users (like relayers) directly interact with Bridge
|
||||
// Hub. They don't need aliasing to operate cross-chain they can operate locally.
|
||||
// Aliasing same account doesn't work on BH.
|
||||
test_cross_chain_alias!(
|
||||
vec![
|
||||
// between AH and BH: denied
|
||||
(AssetHubZagros, BridgeHubZagros, TELEPORT_FEES, DENIED),
|
||||
// between Penpal and BH: denied
|
||||
(PenpalB, BridgeHubZagros, RESERVE_TRANSFER_FEES, DENIED)
|
||||
],
|
||||
origin,
|
||||
target,
|
||||
fees
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn account_on_sibling_syschain_cannot_alias_into_different_local_account() {
|
||||
// origin and target are different accounts on different chains
|
||||
let origin: AccountId = [1; 32].into();
|
||||
let target: AccountId = [2; 32].into();
|
||||
let fees = ZAGROS_ED * 10;
|
||||
|
||||
PenpalB::mint_foreign_asset(
|
||||
<PenpalB as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get()),
|
||||
Location::parent(),
|
||||
origin.clone(),
|
||||
fees * 10,
|
||||
);
|
||||
|
||||
// Aliasing different account on different chains
|
||||
test_cross_chain_alias!(
|
||||
vec![
|
||||
// between AH and BH: denied
|
||||
(AssetHubZagros, BridgeHubZagros, TELEPORT_FEES, DENIED),
|
||||
// between Penpal and BH: denied
|
||||
(PenpalB, BridgeHubZagros, RESERVE_TRANSFER_FEES, DENIED)
|
||||
],
|
||||
origin,
|
||||
target,
|
||||
fees
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aliasing_child_locations() {
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
// Bridge Hub allows aliasing descendant of origin.
|
||||
let origin = Location::new(1, X1([PalletInstance(8)].into()));
|
||||
let target = Location::new(1, X2([PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(8)].into()));
|
||||
let target = Location::new(
|
||||
1,
|
||||
X2([Teyrchain(8), AccountId32 { network: None, id: [1u8; 32] }].into()),
|
||||
);
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(8)].into()));
|
||||
let target =
|
||||
Location::new(1, X3([Teyrchain(8), PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
// Does not allow if not descendant.
|
||||
let origin = Location::new(1, X1([PalletInstance(8)].into()));
|
||||
let target = Location::new(0, X2([PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(8)].into()));
|
||||
let target = Location::new(
|
||||
0,
|
||||
X2([Teyrchain(8), AccountId32 { network: None, id: [1u8; 32] }].into()),
|
||||
);
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(8)].into()));
|
||||
let target = Location::new(0, X1([AccountId32 { network: None, id: [1u8; 32] }].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([AccountId32 { network: None, id: [1u8; 32] }].into()));
|
||||
let target = Location::new(0, X1([AccountId32 { network: None, id: [1u8; 32] }].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn asset_hub_root_aliases_anything() {
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
// Does not allow AH root to alias other locations.
|
||||
let origin = Location::new(1, X1([Teyrchain(1000)].into()));
|
||||
|
||||
let target = Location::new(1, X1([Teyrchain(2000)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(1, X1([AccountId32 { network: None, id: [1u8; 32] }].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(
|
||||
1,
|
||||
X2([Teyrchain(8), AccountId32 { network: None, id: [1u8; 32] }].into()),
|
||||
);
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target =
|
||||
Location::new(1, X3([Teyrchain(42), PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(2, X1([GlobalConsensus(Ethereum { chain_id: 1 })].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(2, X2([GlobalConsensus(Pezkuwi), Teyrchain(1000)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(0, X2([PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
|
||||
// Other AH locations cannot alias anything.
|
||||
let origin = Location::new(1, X2([Teyrchain(1000), GeneralIndex(9)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X2([Teyrchain(1000), PalletInstance(9)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(
|
||||
1,
|
||||
X2([Teyrchain(1000), AccountId32 { network: None, id: [1u8; 32] }].into()),
|
||||
);
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
|
||||
// Other root locations cannot alias anything.
|
||||
let origin = Location::new(1, Here);
|
||||
let target = Location::new(2, X1([GlobalConsensus(Ethereum { chain_id: 1 })].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(2, X2([GlobalConsensus(Pezkuwi), Teyrchain(1000)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(0, X2([PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
|
||||
let origin = Location::new(0, Here);
|
||||
let target = Location::new(1, X1([Teyrchain(2000)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(1001)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(1002)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn authorized_cross_chain_aliases() {
|
||||
// origin and target are different accounts on different chains
|
||||
let origin: AccountId = [100; 32].into();
|
||||
let bad_origin: AccountId = [150; 32].into();
|
||||
let target: AccountId = [200; 32].into();
|
||||
let fees = ZAGROS_ED * 10;
|
||||
|
||||
let pal_admin = <PenpalB as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get());
|
||||
PenpalB::mint_foreign_asset(pal_admin.clone(), Location::parent(), origin.clone(), fees * 10);
|
||||
PenpalB::mint_foreign_asset(pal_admin, Location::parent(), bad_origin.clone(), fees * 10);
|
||||
BridgeHubZagros::fund_accounts(vec![(target.clone(), fees * 10)]);
|
||||
|
||||
// let's authorize `origin` on Penpal to alias `target` on BridgeHub
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
let penpal_origin = Location::new(
|
||||
1,
|
||||
X2([
|
||||
Teyrchain(PenpalB::para_id().into()),
|
||||
AccountId32 {
|
||||
network: Some(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
id: origin.clone().into(),
|
||||
},
|
||||
]
|
||||
.into()),
|
||||
);
|
||||
// `target` adds `penpal_origin` as authorized alias
|
||||
assert_ok!(<BridgeHubZagros as BridgeHubZagrosPallet>::PezkuwiXcm::add_authorized_alias(
|
||||
<BridgeHubZagros as Chain>::RuntimeOrigin::signed(target.clone()),
|
||||
Box::new(penpal_origin.into()),
|
||||
None
|
||||
));
|
||||
});
|
||||
// Verify that unauthorized `bad_origin` cannot alias into `target`, from any chain.
|
||||
test_cross_chain_alias!(
|
||||
vec![
|
||||
// between AH and BridgeHub: denied
|
||||
(AssetHubZagros, BridgeHubZagros, TELEPORT_FEES, DENIED),
|
||||
// between Penpal and BridgeHub: denied
|
||||
(PenpalB, BridgeHubZagros, RESERVE_TRANSFER_FEES, DENIED)
|
||||
],
|
||||
bad_origin,
|
||||
target,
|
||||
fees
|
||||
);
|
||||
// Verify that only authorized `penpal::origin` can alias into `target`, while `origin` on other
|
||||
// chains cannot.
|
||||
test_cross_chain_alias!(
|
||||
vec![
|
||||
// between AH and BridgeHub: denied
|
||||
(AssetHubZagros, BridgeHubZagros, TELEPORT_FEES, DENIED),
|
||||
// between Penpal and BridgeHub: allowed
|
||||
(PenpalB, BridgeHubZagros, RESERVE_TRANSFER_FEES, ALLOWED)
|
||||
],
|
||||
origin,
|
||||
target,
|
||||
fees
|
||||
);
|
||||
// remove authorization for `origin` on Penpal to alias `target` on BridgeHub
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
// `target` removes all authorized aliases
|
||||
assert_ok!(
|
||||
<BridgeHubZagros as BridgeHubZagrosPallet>::PezkuwiXcm::remove_all_authorized_aliases(
|
||||
<BridgeHubZagros as Chain>::RuntimeOrigin::signed(target.clone())
|
||||
)
|
||||
);
|
||||
});
|
||||
// Verify `penpal::origin` can no longer alias into `target` on BridgeHub.
|
||||
test_cross_chain_alias!(
|
||||
vec![(PenpalB, BridgeHubZagros, RESERVE_TRANSFER_FEES, DENIED)],
|
||||
origin,
|
||||
target,
|
||||
fees
|
||||
);
|
||||
}
|
||||
+1599
File diff suppressed because it is too large
Load Diff
+34
@@ -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 = BridgeHubZagrosExistentialDeposit::get();
|
||||
let assets: Assets = (Parent, amount).into();
|
||||
|
||||
test_chain_can_claim_assets!(
|
||||
AssetHubZagros,
|
||||
RuntimeCall,
|
||||
NetworkId::ByGenesis(ZAGROS_GENESIS_HASH),
|
||||
assets,
|
||||
amount
|
||||
);
|
||||
}
|
||||
+241
@@ -0,0 +1,241 @@
|
||||
// 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::snowbridge::{SEPOLIA_ID, WETH};
|
||||
|
||||
mod aliases;
|
||||
mod asset_transfers;
|
||||
mod claim_assets;
|
||||
mod register_bridged_assets;
|
||||
mod send_xcm;
|
||||
mod snowbridge;
|
||||
mod snowbridge_common;
|
||||
// mod snowbridge_v2_inbound;
|
||||
mod snowbridge_edge_case;
|
||||
mod snowbridge_v2_inbound;
|
||||
mod snowbridge_v2_inbound_to_pezkuwichain;
|
||||
mod snowbridge_v2_outbound;
|
||||
mod snowbridge_v2_outbound_edge_case;
|
||||
mod snowbridge_v2_outbound_from_pezkuwichain;
|
||||
mod snowbridge_v2_rewards;
|
||||
mod teleport;
|
||||
mod transact;
|
||||
|
||||
pub(crate) fn asset_hub_pezkuwichain_location() -> Location {
|
||||
Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(ByGenesis(PEZKUWICHAIN_GENESIS_HASH)),
|
||||
Teyrchain(AssetHubPezkuwichain::para_id().into()),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn asset_hub_zagros_global_location() -> Location {
|
||||
Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
Teyrchain(AssetHubZagros::para_id().into()),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn bridge_hub_pezkuwichain_location() -> Location {
|
||||
Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(ByGenesis(PEZKUWICHAIN_GENESIS_HASH)),
|
||||
Teyrchain(BridgeHubPezkuwichain::para_id().into()),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
// ZGR and wWND
|
||||
pub(crate) fn wnd_at_ah_zagros() -> Location {
|
||||
Parent.into()
|
||||
}
|
||||
pub(crate) fn bridged_wnd_at_ah_pezkuwichain() -> Location {
|
||||
Location::new(2, [GlobalConsensus(ByGenesis(ZAGROS_GENESIS_HASH))])
|
||||
}
|
||||
|
||||
// TYR and wTYR
|
||||
pub(crate) fn bridged_roc_at_ah_zagros() -> Location {
|
||||
Location::new(2, [GlobalConsensus(ByGenesis(PEZKUWICHAIN_GENESIS_HASH))])
|
||||
}
|
||||
|
||||
// USDT and wUSDT
|
||||
pub(crate) fn usdt_at_ah_zagros() -> Location {
|
||||
Location::new(0, [PalletInstance(ASSETS_PALLET_ID), GeneralIndex(USDT_ID.into())])
|
||||
}
|
||||
pub(crate) fn bridged_usdt_at_ah_pezkuwichain() -> Location {
|
||||
Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
Teyrchain(AssetHubZagros::para_id().into()),
|
||||
PalletInstance(ASSETS_PALLET_ID),
|
||||
GeneralIndex(USDT_ID.into()),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
// wETH has same relative location on both Pezkuwichain and Zagros AssetHubs
|
||||
pub(crate) fn weth_at_asset_hubs() -> Location {
|
||||
Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(Ethereum { chain_id: SEPOLIA_ID }),
|
||||
AccountKey20 { network: None, key: WETH },
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn create_foreign_on_ah_pezkuwichain(
|
||||
id: v5::Location,
|
||||
sufficient: bool,
|
||||
reserves: Vec<ForeignAssetReserveData>,
|
||||
) {
|
||||
let owner = AssetHubPezkuwichain::account_id_of(ALICE);
|
||||
AssetHubPezkuwichain::force_create_foreign_asset(
|
||||
id.clone(),
|
||||
owner.clone(),
|
||||
sufficient,
|
||||
ASSET_MIN_BALANCE,
|
||||
vec![],
|
||||
);
|
||||
AssetHubPezkuwichain::set_foreign_asset_reserves(id, owner, reserves);
|
||||
}
|
||||
|
||||
pub(crate) fn create_foreign_on_ah_zagros(
|
||||
id: v5::Location,
|
||||
sufficient: bool,
|
||||
reserves: Vec<ForeignAssetReserveData>,
|
||||
prefund_accounts: Vec<(AccountId, u128)>,
|
||||
) {
|
||||
let owner = AssetHubZagros::account_id_of(ALICE);
|
||||
let min = ASSET_MIN_BALANCE;
|
||||
AssetHubZagros::force_create_foreign_asset(
|
||||
id.clone(),
|
||||
owner.clone(),
|
||||
sufficient,
|
||||
min,
|
||||
prefund_accounts,
|
||||
);
|
||||
AssetHubZagros::set_foreign_asset_reserves(id, owner, reserves);
|
||||
}
|
||||
|
||||
pub(crate) fn foreign_balance_on_ah_pezkuwichain(id: v5::Location, who: &AccountId) -> u128 {
|
||||
AssetHubPezkuwichain::execute_with(|| {
|
||||
type Assets = <AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::ForeignAssets;
|
||||
<Assets as Inspect<_>>::balance(id, who)
|
||||
})
|
||||
}
|
||||
pub(crate) fn foreign_balance_on_ah_zagros(id: v5::Location, who: &AccountId) -> u128 {
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type Assets = <AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets;
|
||||
<Assets as Inspect<_>>::balance(id, who)
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn send_assets_from_asset_hub_zagros(
|
||||
destination: Location,
|
||||
assets: Assets,
|
||||
fee_idx: u32,
|
||||
// For knowing what reserve to pick.
|
||||
// We only allow using the same transfer type for assets and fees right now.
|
||||
// And only `LocalReserve` or `DestinationReserve`.
|
||||
transfer_type: TransferType,
|
||||
) -> DispatchResult {
|
||||
let signed_origin =
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(AssetHubZagrosSender::get().into());
|
||||
let beneficiary: Location =
|
||||
AccountId32Junction { network: None, id: AssetHubPezkuwichainReceiver::get().into() }
|
||||
.into();
|
||||
|
||||
type Runtime = <AssetHubZagros as Chain>::Runtime;
|
||||
let remote_fee_id: AssetId = assets
|
||||
.clone()
|
||||
.into_inner()
|
||||
.get(fee_idx as usize)
|
||||
.ok_or(pezpallet_xcm::Error::<Runtime>::Empty)?
|
||||
.clone()
|
||||
.id;
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
<AssetHubZagros as AssetHubZagrosPallet>::PezkuwiXcm::transfer_assets_using_type_and_then(
|
||||
signed_origin,
|
||||
bx!(destination.into()),
|
||||
bx!(assets.into()),
|
||||
bx!(transfer_type.clone()),
|
||||
bx!(remote_fee_id.into()),
|
||||
bx!(transfer_type),
|
||||
bx!(VersionedXcm::from(
|
||||
Xcm::<()>::builder_unsafe().deposit_asset(AllCounted(1), beneficiary).build()
|
||||
)),
|
||||
WeightLimit::Unlimited,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn assert_bridge_hub_zagros_message_accepted(expected_processed: bool) {
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
|
||||
if expected_processed {
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![
|
||||
// pay for bridge fees
|
||||
RuntimeEvent::Balances(pezpallet_balances::Event::Burned { .. }) => {},
|
||||
// message exported
|
||||
RuntimeEvent::BridgePezkuwichainMessages(
|
||||
pezpallet_bridge_messages::Event::MessageAccepted { .. }
|
||||
) => {},
|
||||
// message processed successfully
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
} else {
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed {
|
||||
success: false,
|
||||
..
|
||||
}) => {},
|
||||
]
|
||||
);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn assert_bridge_hub_pezkuwichain_message_received() {
|
||||
BridgeHubPezkuwichain::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubPezkuwichain as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
BridgeHubPezkuwichain,
|
||||
vec![
|
||||
// message sent to destination
|
||||
RuntimeEvent::XcmpQueue(
|
||||
cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
})
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
// 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::*, tests::*};
|
||||
|
||||
const XCM_FEE: u128 = 40_000_000_000;
|
||||
|
||||
/// Tests the registering of a Zagros Asset as a bridged asset on Pezkuwichain Asset Hub.
|
||||
#[test]
|
||||
fn register_zagros_asset_on_rah_from_wah() {
|
||||
// Zagros Asset Hub asset when bridged to Pezkuwichain Asset Hub.
|
||||
let bridged_asset_at_rah = Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
Teyrchain(AssetHubZagros::para_id().into()),
|
||||
PalletInstance(ASSETS_PALLET_ID),
|
||||
GeneralIndex(ASSET_ID.into()),
|
||||
],
|
||||
);
|
||||
// Register above asset on Pezkuwichain AH from Zagros AH.
|
||||
register_asset_on_rah_from_wah(bridged_asset_at_rah);
|
||||
}
|
||||
|
||||
/// Tests the registering of an Ethereum Asset as a bridged asset on Pezkuwichain Asset Hub.
|
||||
#[test]
|
||||
fn register_ethereum_asset_on_rah_from_wah() {
|
||||
// Ethereum asset when bridged to Pezkuwichain Asset Hub.
|
||||
let token_id = H160::random();
|
||||
let bridged_asset_at_rah = Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(Ethereum { chain_id: SEPOLIA_ID }),
|
||||
AccountKey20 { network: None, key: token_id.into() },
|
||||
],
|
||||
);
|
||||
// Register above asset on Pezkuwichain AH from Zagros AH.
|
||||
register_asset_on_rah_from_wah(bridged_asset_at_rah);
|
||||
}
|
||||
|
||||
fn register_asset_on_rah_from_wah(bridged_asset_at_rah: Location) {
|
||||
let sa_of_wah_on_rah =
|
||||
AssetHubPezkuwichain::sovereign_account_of_teyrchain_on_other_global_consensus(
|
||||
ByGenesis(ZAGROS_GENESIS_HASH),
|
||||
AssetHubZagros::para_id(),
|
||||
);
|
||||
|
||||
// Encoded `create_asset` call to be executed in Pezkuwichain Asset Hub ForeignAssets pallet.
|
||||
let call = AssetHubPezkuwichain::create_foreign_asset_call(
|
||||
bridged_asset_at_rah.clone(),
|
||||
ASSET_MIN_BALANCE,
|
||||
sa_of_wah_on_rah.clone(),
|
||||
);
|
||||
|
||||
let origin_kind = OriginKind::Xcm;
|
||||
let fee_amount = XCM_FEE;
|
||||
let fees = (Parent, fee_amount).into();
|
||||
|
||||
let xcm = xcm_transact_paid_execution(call, origin_kind, fees, sa_of_wah_on_rah.clone());
|
||||
|
||||
// SA-of-WAH-on-RAH needs to have balance to pay for fees and asset creation deposit
|
||||
AssetHubPezkuwichain::fund_accounts(vec![(
|
||||
sa_of_wah_on_rah.clone(),
|
||||
ASSET_HUB_PEZKUWICHAIN_ED * 10000000000,
|
||||
)]);
|
||||
|
||||
let destination = asset_hub_pezkuwichain_location();
|
||||
|
||||
// fund the WAH's SA on WBH for paying bridge delivery fees
|
||||
BridgeHubZagros::fund_para_sovereign(AssetHubZagros::para_id(), 10_000_000_000_000u128);
|
||||
|
||||
// set XCM versions
|
||||
AssetHubZagros::force_xcm_version(destination.clone(), XCM_VERSION);
|
||||
BridgeHubZagros::force_xcm_version(bridge_hub_pezkuwichain_location(), XCM_VERSION);
|
||||
|
||||
let root_origin = <AssetHubZagros as Chain>::RuntimeOrigin::root();
|
||||
AssetHubZagros::execute_with(|| {
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::PezkuwiXcm::send(
|
||||
root_origin,
|
||||
bx!(destination.into()),
|
||||
bx!(xcm),
|
||||
));
|
||||
|
||||
AssetHubZagros::assert_xcm_pallet_sent();
|
||||
});
|
||||
|
||||
assert_bridge_hub_zagros_message_accepted(true);
|
||||
assert_bridge_hub_pezkuwichain_message_received();
|
||||
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 == sa_of_wah_on_rah.clone(),
|
||||
amount: *amount == fee_amount,
|
||||
},
|
||||
// Foreign Asset created
|
||||
RuntimeEvent::ForeignAssets(pezpallet_assets::Event::Created { asset_id, creator, owner }) => {
|
||||
asset_id: asset_id == &bridged_asset_at_rah,
|
||||
creator: *creator == sa_of_wah_on_rah.clone(),
|
||||
owner: *owner == sa_of_wah_on_rah,
|
||||
},
|
||||
// Unspent fee minted to origin
|
||||
RuntimeEvent::Balances(pezpallet_balances::Event::Minted { who, .. }) => {
|
||||
who: *who == sa_of_wah_on_rah.clone(),
|
||||
},
|
||||
]
|
||||
);
|
||||
type ForeignAssets = <AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::ForeignAssets;
|
||||
assert!(ForeignAssets::asset_exists(bridged_asset_at_rah));
|
||||
});
|
||||
}
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
// 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 emulated_integration_tests_common::xcm_helpers::{
|
||||
find_mq_processed_id, find_xcm_sent_message_id,
|
||||
};
|
||||
use pezkuwichain_zagros_system_emulated_network::zagros_emulated_chain::zagros_runtime::Dmp;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::tests::*;
|
||||
|
||||
#[test]
|
||||
fn send_xcm_from_zagros_relay_to_pezkuwichain_asset_hub_should_fail_on_not_applicable() {
|
||||
// Init tests variables
|
||||
// XcmPallet send arguments
|
||||
let sudo_origin = <Zagros as Chain>::RuntimeOrigin::root();
|
||||
let destination = Zagros::child_location_of(BridgeHubZagros::para_id()).into();
|
||||
let weight_limit = WeightLimit::Unlimited;
|
||||
let check_origin = None;
|
||||
|
||||
let remote_xcm = Xcm(vec![ClearOrigin]);
|
||||
|
||||
let xcm = VersionedXcm::from(Xcm(vec![
|
||||
UnpaidExecution { weight_limit, check_origin },
|
||||
ExportMessage {
|
||||
network: ByGenesis(PEZKUWICHAIN_GENESIS_HASH),
|
||||
destination: [Teyrchain(AssetHubPezkuwichain::para_id().into())].into(),
|
||||
xcm: remote_xcm,
|
||||
},
|
||||
]));
|
||||
|
||||
// Zagros Global Consensus
|
||||
// Send XCM message from Relay Chain to Bridge Hub source Teyrchain
|
||||
Zagros::execute_with(|| {
|
||||
Dmp::make_teyrchain_reachable(BridgeHubZagros::para_id());
|
||||
|
||||
assert_ok!(<Zagros as ZagrosPallet>::XcmPallet::send(
|
||||
sudo_origin,
|
||||
bx!(destination),
|
||||
bx!(xcm),
|
||||
));
|
||||
|
||||
type RuntimeEvent = <Zagros as Chain>::RuntimeEvent;
|
||||
|
||||
assert_expected_events!(
|
||||
Zagros,
|
||||
vec![
|
||||
RuntimeEvent::XcmPallet(pezpallet_xcm::Event::Sent { .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
// Receive XCM message in Bridge Hub source Teyrchain, it should fail, because we don't have
|
||||
// opened bridge/lane.
|
||||
assert_bridge_hub_zagros_message_accepted(false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_xcm_through_opened_lane_with_different_xcm_version_on_hops_works() {
|
||||
// prepare data
|
||||
let destination = asset_hub_pezkuwichain_location();
|
||||
let native_token = Location::parent();
|
||||
let amount = ASSET_HUB_ZAGROS_ED * 1_000;
|
||||
|
||||
// fund the AHR's SA on BHR for paying bridge delivery fees
|
||||
BridgeHubZagros::fund_para_sovereign(AssetHubZagros::para_id(), 10_000_000_000_000u128);
|
||||
// fund sender
|
||||
AssetHubZagros::fund_accounts(vec![(AssetHubZagrosSender::get().into(), amount * 10)]);
|
||||
|
||||
// Initially set only default version on all runtimes
|
||||
let newer_xcm_version = xcm::prelude::XCM_VERSION;
|
||||
let older_xcm_version = newer_xcm_version - 1;
|
||||
AssetHubPezkuwichain::force_default_xcm_version(Some(older_xcm_version));
|
||||
BridgeHubPezkuwichain::force_default_xcm_version(Some(older_xcm_version));
|
||||
BridgeHubZagros::force_default_xcm_version(Some(older_xcm_version));
|
||||
AssetHubZagros::force_default_xcm_version(Some(older_xcm_version));
|
||||
|
||||
// send XCM from AssetHubZagros - fails - destination version not known
|
||||
assert_err!(
|
||||
send_assets_from_asset_hub_zagros(
|
||||
destination.clone(),
|
||||
(native_token.clone(), amount).into(),
|
||||
0,
|
||||
TransferType::LocalReserve
|
||||
),
|
||||
DispatchError::Module(pezsp_runtime::ModuleError {
|
||||
index: 31,
|
||||
error: [1, 0, 0, 0],
|
||||
message: Some("SendFailure")
|
||||
})
|
||||
);
|
||||
|
||||
// set destination version
|
||||
AssetHubZagros::force_xcm_version(destination.clone(), newer_xcm_version);
|
||||
|
||||
// set version with `ExportMessage` for BridgeHubZagros
|
||||
AssetHubZagros::force_xcm_version(
|
||||
ParentThen(Teyrchain(BridgeHubZagros::para_id().into()).into()).into(),
|
||||
newer_xcm_version,
|
||||
);
|
||||
// send XCM from AssetHubZagros - ok
|
||||
assert_ok!(send_assets_from_asset_hub_zagros(
|
||||
destination.clone(),
|
||||
(native_token.clone(), amount).into(),
|
||||
0,
|
||||
TransferType::LocalReserve
|
||||
));
|
||||
|
||||
// `ExportMessage` on local BridgeHub - fails - remote BridgeHub version not known
|
||||
assert_bridge_hub_zagros_message_accepted(false);
|
||||
|
||||
// set version for remote BridgeHub on BridgeHubZagros
|
||||
BridgeHubZagros::force_xcm_version(bridge_hub_pezkuwichain_location(), newer_xcm_version);
|
||||
// set version for AssetHubPezkuwichain on BridgeHubPezkuwichain
|
||||
BridgeHubPezkuwichain::force_xcm_version(
|
||||
ParentThen(Teyrchain(AssetHubPezkuwichain::para_id().into()).into()).into(),
|
||||
newer_xcm_version,
|
||||
);
|
||||
|
||||
// send XCM from AssetHubZagros - ok
|
||||
assert_ok!(send_assets_from_asset_hub_zagros(
|
||||
destination.clone(),
|
||||
(native_token.clone(), amount).into(),
|
||||
0,
|
||||
TransferType::LocalReserve
|
||||
));
|
||||
assert_bridge_hub_zagros_message_accepted(true);
|
||||
assert_bridge_hub_pezkuwichain_message_received();
|
||||
// message delivered and processed at destination
|
||||
AssetHubPezkuwichain::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubPezkuwichain as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubPezkuwichain,
|
||||
vec![
|
||||
// message processed with failure, but for this scenario it is ok, important is that was delivered
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: false, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn xcm_persists_set_topic_across_hops() {
|
||||
for test_topic_id in [Some([42; 32]), None] {
|
||||
// Reset tracked topic state before each run
|
||||
let mut tracked_topic_ids = HashMap::new();
|
||||
|
||||
// Prepare test input
|
||||
let sudo_origin = <Zagros as Chain>::RuntimeOrigin::root();
|
||||
let destination = Zagros::child_location_of(BridgeHubZagros::para_id()).into();
|
||||
let weight_limit = Unlimited;
|
||||
let check_origin = None;
|
||||
|
||||
// Construct XCM with optional SetTopic
|
||||
let mut message = vec![UnpaidExecution { weight_limit, check_origin }, ClearOrigin];
|
||||
if let Some(topic_id) = test_topic_id {
|
||||
message.push(SetTopic(topic_id));
|
||||
}
|
||||
let xcm = VersionedXcm::from(Xcm(message));
|
||||
|
||||
// Send XCM from Zagros to BridgeHubZagros
|
||||
Zagros::execute_with(|| {
|
||||
Dmp::make_teyrchain_reachable(BridgeHubZagros::para_id());
|
||||
assert_ok!(<Zagros as ZagrosPallet>::XcmPallet::send(
|
||||
sudo_origin.clone(),
|
||||
bx!(destination),
|
||||
bx!(xcm),
|
||||
));
|
||||
|
||||
let msg_sent_id = find_xcm_sent_message_id::<Zagros>().expect("Missing Sent Event");
|
||||
tracked_topic_ids.insert("Zagros", msg_sent_id.into());
|
||||
});
|
||||
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
let mq_prc_id =
|
||||
find_mq_processed_id::<BridgeHubZagros>().expect("Missing Processed Event");
|
||||
tracked_topic_ids.insert("BridgeHubZagros", mq_prc_id);
|
||||
});
|
||||
|
||||
// Assert exactly one consistent topic ID across all hops
|
||||
let topic_id = tracked_topic_ids.get("Zagros");
|
||||
assert_eq!(tracked_topic_ids.get("BridgeHubZagros"), topic_id);
|
||||
if let Some(expected) = test_topic_id {
|
||||
assert_eq!(topic_id, Some(&expected.into()));
|
||||
}
|
||||
}
|
||||
}
|
||||
+2294
File diff suppressed because it is too large
Load Diff
+544
@@ -0,0 +1,544 @@
|
||||
// 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::*, tests::bridged_roc_at_ah_zagros};
|
||||
use asset_hub_zagros_runtime::xcm_config::LocationToAccountId;
|
||||
use emulated_integration_tests_common::{
|
||||
snowbridge::{SEPOLIA_ID, WETH},
|
||||
PenpalBTeleportableAssetLocation,
|
||||
};
|
||||
use pezframe_support::traits::fungibles::Mutate;
|
||||
use hex_literal::hex;
|
||||
use pezkuwichain_zagros_system_emulated_network::penpal_emulated_chain::{
|
||||
penpal_runtime::xcm_config::{CheckingAccount, TELEPORTABLE_ASSET_ID},
|
||||
PenpalAssetOwner,
|
||||
};
|
||||
use snowbridge_core::AssetMetadata;
|
||||
use pezsp_core::H160;
|
||||
use testnet_teyrchains_constants::zagros::snowbridge::EthereumNetwork;
|
||||
use xcm_builder::ExternalConsensusLocationsConverterFor;
|
||||
use xcm_executor::traits::ConvertLocation;
|
||||
|
||||
pub const INITIAL_FUND: u128 = 50_000_000_000_000;
|
||||
pub const ETHEREUM_DESTINATION_ADDRESS: [u8; 20] = hex!("44a57ee2f2FCcb85FDa2B0B18EBD0D8D2333700e");
|
||||
pub const AGENT_ADDRESS: [u8; 20] = hex!("90A987B944Cb1dCcE5564e5FDeCD7a54D3de27Fe");
|
||||
pub const TOKEN_AMOUNT: u128 = 10_000_000_000_000;
|
||||
pub const REMOTE_FEE_AMOUNT_IN_ETHER: u128 = 600_000_000_000;
|
||||
pub const LOCAL_FEE_AMOUNT_IN_DOT: u128 = 800_000_000_000;
|
||||
|
||||
pub const EXECUTION_WEIGHT: u64 = 8_000_000_000;
|
||||
|
||||
pub fn beneficiary() -> Location {
|
||||
Location::new(0, [AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }])
|
||||
}
|
||||
|
||||
pub fn asset_hub() -> Location {
|
||||
Location::new(1, Teyrchain(AssetHubZagros::para_id().into()))
|
||||
}
|
||||
|
||||
pub fn bridge_hub() -> Location {
|
||||
Location::new(1, Teyrchain(BridgeHubZagros::para_id().into()))
|
||||
}
|
||||
|
||||
pub fn fund_on_bh() {
|
||||
let assethub_sovereign = BridgeHubZagros::sovereign_account_id_of(asset_hub());
|
||||
BridgeHubZagros::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]);
|
||||
}
|
||||
|
||||
pub fn register_assets_on_ah() {}
|
||||
pub fn register_relay_token_on_bh() {
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
type RuntimeOrigin = <BridgeHubZagros as Chain>::RuntimeOrigin;
|
||||
|
||||
// Register ZGR on BH
|
||||
assert_ok!(<BridgeHubZagros as BridgeHubZagrosPallet>::EthereumSystem::register_token(
|
||||
RuntimeOrigin::root(),
|
||||
Box::new(VersionedLocation::from(Location::parent())),
|
||||
AssetMetadata {
|
||||
name: "wnd".as_bytes().to_vec().try_into().unwrap(),
|
||||
symbol: "wnd".as_bytes().to_vec().try_into().unwrap(),
|
||||
decimals: 12,
|
||||
},
|
||||
));
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![RuntimeEvent::EthereumSystem(snowbridge_pallet_system::Event::RegisterToken { .. }) => {},]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn register_assets_on_penpal() {
|
||||
let ethereum_sovereign: AccountId = snowbridge_sovereign();
|
||||
PenpalB::execute_with(|| {
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::ForeignAssets::force_create(
|
||||
<PenpalB as Chain>::RuntimeOrigin::root(),
|
||||
weth_location().try_into().unwrap(),
|
||||
ethereum_sovereign.clone().into(),
|
||||
true,
|
||||
1,
|
||||
));
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::ForeignAssets::force_create(
|
||||
<PenpalB as Chain>::RuntimeOrigin::root(),
|
||||
ethereum().try_into().unwrap(),
|
||||
ethereum_sovereign.into(),
|
||||
true,
|
||||
1,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
pub fn register_foreign_asset(token_location: Location) {
|
||||
let bridge_owner = snowbridge_sovereign();
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeOrigin = <AssetHubZagros as Chain>::RuntimeOrigin;
|
||||
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::force_create(
|
||||
RuntimeOrigin::root(),
|
||||
token_location.clone().try_into().unwrap(),
|
||||
bridge_owner.clone().into(),
|
||||
true,
|
||||
1000,
|
||||
));
|
||||
assert!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::asset_exists(
|
||||
token_location.clone().try_into().unwrap(),
|
||||
));
|
||||
});
|
||||
AssetHubZagros::set_foreign_asset_reserves(
|
||||
token_location,
|
||||
bridge_owner,
|
||||
vec![(ethereum(), false).into()],
|
||||
);
|
||||
}
|
||||
|
||||
pub fn register_pal_on_ah() {
|
||||
// Create PAL(i.e. native asset for penpal) on AH.
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeOrigin = <AssetHubZagros as Chain>::RuntimeOrigin;
|
||||
let penpal_asset_id = Location::new(1, Teyrchain(PenpalB::para_id().into()));
|
||||
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::force_create(
|
||||
RuntimeOrigin::root(),
|
||||
penpal_asset_id.clone(),
|
||||
PenpalAssetOwner::get().into(),
|
||||
false,
|
||||
1_000_000,
|
||||
));
|
||||
|
||||
assert!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::asset_exists(
|
||||
penpal_asset_id.clone(),
|
||||
));
|
||||
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::mint_into(
|
||||
penpal_asset_id.clone(),
|
||||
&AssetHubZagrosReceiver::get(),
|
||||
TOKEN_AMOUNT,
|
||||
));
|
||||
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::mint_into(
|
||||
penpal_asset_id.clone(),
|
||||
&AssetHubZagrosSender::get(),
|
||||
TOKEN_AMOUNT,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
pub fn penpal_root_sovereign() -> pezsp_runtime::AccountId32 {
|
||||
let penpal_root_sovereign: AccountId = PenpalB::execute_with(|| {
|
||||
use pezkuwichain_zagros_system_emulated_network::penpal_emulated_chain::penpal_runtime::xcm_config;
|
||||
xcm_config::LocationToAccountId::convert_location(&xcm_config::RootLocation::get())
|
||||
.unwrap()
|
||||
.into()
|
||||
});
|
||||
penpal_root_sovereign
|
||||
}
|
||||
|
||||
pub fn fund_on_penpal() {
|
||||
let sudo_account = penpal_root_sovereign();
|
||||
PenpalB::fund_accounts(vec![
|
||||
(PenpalBReceiver::get(), INITIAL_FUND),
|
||||
(PenpalBSender::get(), INITIAL_FUND),
|
||||
(CheckingAccount::get(), INITIAL_FUND),
|
||||
(sudo_account.clone(), INITIAL_FUND),
|
||||
]);
|
||||
PenpalB::execute_with(|| {
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::ForeignAssets::mint_into(
|
||||
Location::parent(),
|
||||
&PenpalBReceiver::get(),
|
||||
INITIAL_FUND,
|
||||
));
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::ForeignAssets::mint_into(
|
||||
Location::parent(),
|
||||
&PenpalBSender::get(),
|
||||
INITIAL_FUND,
|
||||
));
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::ForeignAssets::mint_into(
|
||||
Location::parent(),
|
||||
&sudo_account,
|
||||
INITIAL_FUND,
|
||||
));
|
||||
});
|
||||
PenpalB::execute_with(|| {
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::Assets::mint_into(
|
||||
TELEPORTABLE_ASSET_ID,
|
||||
&PenpalBReceiver::get(),
|
||||
INITIAL_FUND,
|
||||
));
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::Assets::mint_into(
|
||||
TELEPORTABLE_ASSET_ID,
|
||||
&PenpalBSender::get(),
|
||||
INITIAL_FUND,
|
||||
));
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::Assets::mint_into(
|
||||
TELEPORTABLE_ASSET_ID,
|
||||
&sudo_account,
|
||||
INITIAL_FUND,
|
||||
));
|
||||
});
|
||||
PenpalB::execute_with(|| {
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::ForeignAssets::mint_into(
|
||||
weth_location().try_into().unwrap(),
|
||||
&PenpalBReceiver::get(),
|
||||
INITIAL_FUND,
|
||||
));
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::ForeignAssets::mint_into(
|
||||
weth_location().try_into().unwrap(),
|
||||
&PenpalBSender::get(),
|
||||
INITIAL_FUND,
|
||||
));
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::ForeignAssets::mint_into(
|
||||
weth_location().try_into().unwrap(),
|
||||
&sudo_account,
|
||||
INITIAL_FUND,
|
||||
));
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::ForeignAssets::mint_into(
|
||||
ethereum().try_into().unwrap(),
|
||||
&PenpalBReceiver::get(),
|
||||
INITIAL_FUND,
|
||||
));
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::ForeignAssets::mint_into(
|
||||
ethereum().try_into().unwrap(),
|
||||
&PenpalBSender::get(),
|
||||
INITIAL_FUND,
|
||||
));
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::ForeignAssets::mint_into(
|
||||
ethereum().try_into().unwrap(),
|
||||
&sudo_account,
|
||||
INITIAL_FUND,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
pub fn set_trust_reserve_on_penpal() {
|
||||
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(Ethereum { chain_id: SEPOLIA_ID })]).encode(),
|
||||
)],
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
pub fn fund_on_ah() {
|
||||
AssetHubZagros::fund_accounts(vec![(AssetHubZagrosSender::get(), INITIAL_FUND)]);
|
||||
AssetHubZagros::fund_accounts(vec![(AssetHubZagrosReceiver::get(), INITIAL_FUND)]);
|
||||
|
||||
let penpal_sovereign = AssetHubZagros::sovereign_account_id_of(
|
||||
AssetHubZagros::sibling_location_of(PenpalB::para_id()),
|
||||
);
|
||||
let penpal_user_sovereign = LocationToAccountId::convert_location(&Location::new(
|
||||
1,
|
||||
[
|
||||
Teyrchain(PenpalB::para_id().into()),
|
||||
AccountId32 {
|
||||
network: Some(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
id: PenpalBSender::get().into(),
|
||||
},
|
||||
],
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::mint_into(
|
||||
weth_location().try_into().unwrap(),
|
||||
&penpal_sovereign,
|
||||
INITIAL_FUND,
|
||||
));
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::mint_into(
|
||||
weth_location().try_into().unwrap(),
|
||||
&penpal_user_sovereign,
|
||||
INITIAL_FUND,
|
||||
));
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::mint_into(
|
||||
weth_location().try_into().unwrap(),
|
||||
&AssetHubZagrosReceiver::get(),
|
||||
INITIAL_FUND,
|
||||
));
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::mint_into(
|
||||
weth_location().try_into().unwrap(),
|
||||
&AssetHubZagrosSender::get(),
|
||||
INITIAL_FUND,
|
||||
));
|
||||
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::mint_into(
|
||||
ethereum().try_into().unwrap(),
|
||||
&penpal_sovereign,
|
||||
INITIAL_FUND,
|
||||
));
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::mint_into(
|
||||
ethereum().try_into().unwrap(),
|
||||
&penpal_user_sovereign,
|
||||
INITIAL_FUND,
|
||||
));
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::mint_into(
|
||||
ethereum().try_into().unwrap(),
|
||||
&AssetHubZagrosReceiver::get(),
|
||||
INITIAL_FUND,
|
||||
));
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::mint_into(
|
||||
ethereum().try_into().unwrap(),
|
||||
&AssetHubZagrosSender::get(),
|
||||
INITIAL_FUND,
|
||||
));
|
||||
});
|
||||
|
||||
AssetHubZagros::fund_accounts(vec![(snowbridge_sovereign(), INITIAL_FUND)]);
|
||||
AssetHubZagros::fund_accounts(vec![(penpal_sovereign.clone(), INITIAL_FUND)]);
|
||||
AssetHubZagros::fund_accounts(vec![(penpal_user_sovereign.clone(), INITIAL_FUND)]);
|
||||
}
|
||||
|
||||
pub fn create_pools_on_ah() {
|
||||
// We create a pool between ZGR and WETH in AssetHub to support paying for fees with WETH.
|
||||
let ethereum_sovereign = snowbridge_sovereign();
|
||||
AssetHubZagros::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]);
|
||||
PenpalB::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]);
|
||||
create_pool_with_native_on!(
|
||||
AssetHubZagros,
|
||||
weth_location(),
|
||||
true,
|
||||
ethereum_sovereign.clone(),
|
||||
1_000_000_000_000,
|
||||
20_000_000_000
|
||||
);
|
||||
create_pool_with_native_on!(
|
||||
AssetHubZagros,
|
||||
ethereum(),
|
||||
true,
|
||||
ethereum_sovereign.clone(),
|
||||
1_000_000_000_000,
|
||||
20_000_000_000
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn set_up_eth_and_hez_pool() {
|
||||
// We create a pool between ZGR and WETH in AssetHub to support paying for fees with WETH.
|
||||
let ethereum_sovereign = snowbridge_sovereign();
|
||||
AssetHubZagros::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]);
|
||||
PenpalB::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]);
|
||||
create_pool_with_native_on!(AssetHubZagros, eth_location(), true, ethereum_sovereign.clone());
|
||||
}
|
||||
|
||||
pub(crate) fn set_up_eth_and_hez_pool_on_penpal() {
|
||||
let ethereum_sovereign = snowbridge_sovereign();
|
||||
AssetHubZagros::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]);
|
||||
PenpalB::fund_accounts(vec![(ethereum_sovereign.clone(), INITIAL_FUND)]);
|
||||
create_pool_with_native_on!(PenpalB, eth_location(), true, ethereum_sovereign.clone());
|
||||
}
|
||||
|
||||
pub(crate) fn set_up_eth_and_hez_pool_on_pezkuwichain() {
|
||||
let sa_of_wah_on_rah =
|
||||
AssetHubPezkuwichain::sovereign_account_of_teyrchain_on_other_global_consensus(
|
||||
ByGenesis(ZAGROS_GENESIS_HASH),
|
||||
AssetHubZagros::para_id(),
|
||||
);
|
||||
AssetHubPezkuwichain::fund_accounts(vec![(sa_of_wah_on_rah.clone(), INITIAL_FUND)]);
|
||||
create_pool_with_native_on!(
|
||||
AssetHubPezkuwichain,
|
||||
eth_location(),
|
||||
true,
|
||||
sa_of_wah_on_rah.clone()
|
||||
);
|
||||
}
|
||||
|
||||
pub fn register_pal_on_bh() {
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
type RuntimeOrigin = <BridgeHubZagros as Chain>::RuntimeOrigin;
|
||||
|
||||
assert_ok!(<BridgeHubZagros as BridgeHubZagrosPallet>::EthereumSystem::register_token(
|
||||
RuntimeOrigin::root(),
|
||||
Box::new(VersionedLocation::from(PenpalBTeleportableAssetLocation::get())),
|
||||
AssetMetadata {
|
||||
name: "pal".as_bytes().to_vec().try_into().unwrap(),
|
||||
symbol: "pal".as_bytes().to_vec().try_into().unwrap(),
|
||||
decimals: 12,
|
||||
},
|
||||
));
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![RuntimeEvent::EthereumSystem(snowbridge_pallet_system::Event::RegisterToken { .. }) => {},]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn snowbridge_sovereign() -> pezsp_runtime::AccountId32 {
|
||||
use asset_hub_zagros_runtime::xcm_config::UniversalLocation as AssetHubZagrosUniversalLocation;
|
||||
let ethereum_sovereign: AccountId = AssetHubZagros::execute_with(|| {
|
||||
ExternalConsensusLocationsConverterFor::<
|
||||
AssetHubZagrosUniversalLocation,
|
||||
[u8; 32],
|
||||
>::convert_location(&Location::new(
|
||||
2,
|
||||
[xcm::v5::Junction::GlobalConsensus(EthereumNetwork::get())],
|
||||
))
|
||||
.unwrap()
|
||||
.into()
|
||||
});
|
||||
|
||||
ethereum_sovereign
|
||||
}
|
||||
|
||||
pub fn weth_location() -> Location {
|
||||
erc20_token_location(WETH.into())
|
||||
}
|
||||
|
||||
pub fn eth_location() -> Location {
|
||||
Location::new(2, [GlobalConsensus(Ethereum { chain_id: SEPOLIA_ID })])
|
||||
}
|
||||
|
||||
pub fn ethereum() -> Location {
|
||||
eth_location()
|
||||
}
|
||||
|
||||
pub fn erc20_token_location(token_id: H160) -> Location {
|
||||
Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(EthereumNetwork::get().into()),
|
||||
AccountKey20 { network: None, key: token_id.into() },
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
// TYR and wTYR
|
||||
pub(crate) fn roc_at_ah_pezkuwichain() -> Location {
|
||||
Parent.into()
|
||||
}
|
||||
|
||||
// set up pool
|
||||
pub(crate) fn set_up_pool_with_wnd_on_ah_zagros(
|
||||
asset: Location,
|
||||
is_foreign: bool,
|
||||
initial_fund: u128,
|
||||
initial_liquidity: u128,
|
||||
) {
|
||||
let wnd: Location = Parent.into();
|
||||
AssetHubZagros::fund_accounts(vec![(AssetHubZagrosSender::get(), initial_fund)]);
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
let owner = AssetHubZagrosSender::get();
|
||||
let signed_owner = <AssetHubZagros as Chain>::RuntimeOrigin::signed(owner.clone());
|
||||
|
||||
if is_foreign {
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::mint(
|
||||
signed_owner.clone(),
|
||||
asset.clone().into(),
|
||||
owner.clone().into(),
|
||||
initial_fund,
|
||||
));
|
||||
} else {
|
||||
let asset_id = match asset.interior.last() {
|
||||
Some(GeneralIndex(id)) => *id as u32,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::Assets::mint(
|
||||
signed_owner.clone(),
|
||||
asset_id.into(),
|
||||
owner.clone().into(),
|
||||
initial_fund,
|
||||
));
|
||||
}
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::AssetConversion::create_pool(
|
||||
signed_owner.clone(),
|
||||
Box::new(wnd.clone()),
|
||||
Box::new(asset.clone()),
|
||||
));
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::AssetConversion(pezpallet_asset_conversion::Event::PoolCreated { .. }) => {},
|
||||
]
|
||||
);
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::AssetConversion::add_liquidity(
|
||||
signed_owner.clone(),
|
||||
Box::new(wnd),
|
||||
Box::new(asset),
|
||||
initial_liquidity,
|
||||
initial_liquidity,
|
||||
1,
|
||||
1,
|
||||
owner.into()
|
||||
));
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::AssetConversion(pezpallet_asset_conversion::Event::LiquidityAdded {..}) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn register_roc_on_bh() {
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
type RuntimeOrigin = <BridgeHubZagros as Chain>::RuntimeOrigin;
|
||||
|
||||
// Register TYR on BH
|
||||
assert_ok!(<BridgeHubZagros as BridgeHubZagrosPallet>::EthereumSystem::register_token(
|
||||
RuntimeOrigin::root(),
|
||||
Box::new(VersionedLocation::from(bridged_roc_at_ah_zagros())),
|
||||
AssetMetadata {
|
||||
name: "roc".as_bytes().to_vec().try_into().unwrap(),
|
||||
symbol: "roc".as_bytes().to_vec().try_into().unwrap(),
|
||||
decimals: 12,
|
||||
},
|
||||
));
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![RuntimeEvent::EthereumSystem(snowbridge_pallet_system::Event::RegisterToken { .. }) => {},]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn asset_hub_zagros_global_location() -> Location {
|
||||
Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
Teyrchain(AssetHubZagros::para_id().into()),
|
||||
],
|
||||
)
|
||||
}
|
||||
pub(crate) fn bridge_hub_zagros_location() -> Location {
|
||||
Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
Teyrchain(BridgeHubZagros::para_id().into()),
|
||||
],
|
||||
)
|
||||
}
|
||||
+296
@@ -0,0 +1,296 @@
|
||||
// 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::*, tests::snowbridge_common::*};
|
||||
use bridge_hub_zagros_runtime::xcm_config::LocationToAccountId;
|
||||
use emulated_integration_tests_common::snowbridge::{SEPOLIA_ID, WETH};
|
||||
use snowbridge_core::AssetMetadata;
|
||||
use snowbridge_pallet_system::Error;
|
||||
use testnet_teyrchains_constants::zagros::snowbridge::EthereumNetwork;
|
||||
use xcm_executor::traits::ConvertLocation;
|
||||
|
||||
// The user origin should be banned in ethereum_blob_exporter with error logs
|
||||
// xcm::ethereum_blob_exporter: could not get teyrchain id from universal source
|
||||
// 'X2([Teyrchain(1000), AccountId32 {...}])'
|
||||
#[test]
|
||||
fn user_send_message_directly_bypass_exporter_from_ah_will_fail() {
|
||||
fund_on_bh();
|
||||
register_assets_on_ah();
|
||||
fund_on_ah();
|
||||
create_pools_on_ah();
|
||||
|
||||
let sov_account_for_sender = LocationToAccountId::convert_location(&Location::new(
|
||||
1,
|
||||
[
|
||||
Teyrchain(AssetHubZagros::para_id().into()),
|
||||
AccountId32 {
|
||||
network: Some(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
id: AssetHubZagrosSender::get().into(),
|
||||
},
|
||||
],
|
||||
))
|
||||
.unwrap();
|
||||
BridgeHubZagros::fund_accounts(vec![(sov_account_for_sender, INITIAL_FUND)]);
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
type RuntimeOrigin = <AssetHubZagros as Chain>::RuntimeOrigin;
|
||||
|
||||
let local_fee_asset =
|
||||
Asset { id: AssetId(Location::parent()), fun: Fungible(1_000_000_000_000) };
|
||||
|
||||
let weth_location_reanchored =
|
||||
Location::new(0, [AccountKey20 { network: None, key: WETH.into() }]);
|
||||
|
||||
let weth_asset = Asset {
|
||||
id: AssetId(weth_location_reanchored.clone()),
|
||||
fun: Fungible(TOKEN_AMOUNT * 1_000_000_000),
|
||||
};
|
||||
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::PezkuwiXcm::send(
|
||||
RuntimeOrigin::signed(AssetHubZagrosSender::get()),
|
||||
bx!(VersionedLocation::from(bridge_hub())),
|
||||
bx!(VersionedXcm::from(Xcm(vec![
|
||||
WithdrawAsset(local_fee_asset.clone().into()),
|
||||
BuyExecution { fees: local_fee_asset.clone(), weight_limit: Unlimited },
|
||||
ExportMessage {
|
||||
network: Ethereum { chain_id: SEPOLIA_ID },
|
||||
destination: Here,
|
||||
xcm: Xcm(vec![
|
||||
WithdrawAsset(weth_asset.clone().into()),
|
||||
DepositAsset { assets: Wild(All), beneficiary: beneficiary() },
|
||||
SetTopic([0; 32]),
|
||||
]),
|
||||
},
|
||||
]))),
|
||||
));
|
||||
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![RuntimeEvent::PezkuwiXcm(pezpallet_xcm::Event::Sent{ .. }) => {},]
|
||||
);
|
||||
});
|
||||
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed{ success:false, .. }) => {},]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// ENA is not allowed to be registered as PNA
|
||||
#[test]
|
||||
fn test_register_ena_on_bh_will_fail() {
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeOrigin = <BridgeHubZagros as Chain>::RuntimeOrigin;
|
||||
type Runtime = <BridgeHubZagros as Chain>::Runtime;
|
||||
|
||||
assert_ok!(<BridgeHubZagros as BridgeHubZagrosPallet>::Balances::force_set_balance(
|
||||
RuntimeOrigin::root(),
|
||||
pezsp_runtime::MultiAddress::Id(BridgeHubZagrosSender::get()),
|
||||
INITIAL_FUND * 10,
|
||||
));
|
||||
|
||||
assert_err!(
|
||||
<BridgeHubZagros as BridgeHubZagrosPallet>::EthereumSystem::register_token(
|
||||
RuntimeOrigin::root(),
|
||||
Box::new(VersionedLocation::from(weth_location())),
|
||||
AssetMetadata {
|
||||
name: "weth".as_bytes().to_vec().try_into().unwrap(),
|
||||
symbol: "weth".as_bytes().to_vec().try_into().unwrap(),
|
||||
decimals: 18,
|
||||
},
|
||||
),
|
||||
Error::<Runtime>::LocationConversionFailed
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn user_exploit_with_arbitrary_message_will_fail() {
|
||||
fund_on_bh();
|
||||
register_assets_on_ah();
|
||||
fund_on_ah();
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
type RuntimeOrigin = <AssetHubZagros as Chain>::RuntimeOrigin;
|
||||
|
||||
let remote_fee_asset_location: Location =
|
||||
Location::new(2, [EthereumNetwork::get().into()]).into();
|
||||
|
||||
let remote_fee_asset: Asset = (remote_fee_asset_location.clone(), 1).into();
|
||||
|
||||
let assets = VersionedAssets::from(vec![remote_fee_asset]);
|
||||
|
||||
let exploited_weth = Asset {
|
||||
id: AssetId(Location::new(0, [AccountKey20 { network: None, key: WETH.into() }])),
|
||||
// A big amount without burning
|
||||
fun: Fungible(TOKEN_AMOUNT * 1_000_000_000),
|
||||
};
|
||||
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::PezkuwiXcm::transfer_assets_using_type_and_then(
|
||||
RuntimeOrigin::signed(AssetHubZagrosSender::get()),
|
||||
bx!(VersionedLocation::from(ethereum())),
|
||||
bx!(assets),
|
||||
bx!(TransferType::DestinationReserve),
|
||||
bx!(VersionedAssetId::from(remote_fee_asset_location.clone())),
|
||||
bx!(TransferType::DestinationReserve),
|
||||
// exploited_weth here is far more than the burnt, which means instructions inner
|
||||
// are user provided and untrustworthy/dangerous!
|
||||
// Currently it depends on EthereumBlobExporter on BH to check the message is legal
|
||||
// and convert to Ethereum command.
|
||||
bx!(VersionedXcm::from(Xcm(vec![
|
||||
WithdrawAsset(exploited_weth.clone().into()),
|
||||
DepositAsset { assets: Wild(All), beneficiary: beneficiary() },
|
||||
SetTopic([0; 32]),
|
||||
]))),
|
||||
Unlimited
|
||||
));
|
||||
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![RuntimeEvent::PezkuwiXcm(pezpallet_xcm::Event::Sent{ .. }) => {},]
|
||||
);
|
||||
});
|
||||
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed{ success:false, .. }) => {},]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn export_from_system_teyrchain_but_not_root_will_fail() {
|
||||
fund_on_bh();
|
||||
register_assets_on_ah();
|
||||
fund_on_ah();
|
||||
create_pools_on_ah();
|
||||
|
||||
let sub_location = PalletInstance(100);
|
||||
let assethub_pallet_sovereign = BridgeHubZagros::sovereign_account_id_of(Location::new(
|
||||
1,
|
||||
[Teyrchain(AssetHubZagros::para_id().into()), sub_location],
|
||||
));
|
||||
BridgeHubZagros::fund_accounts(vec![(assethub_pallet_sovereign.clone(), INITIAL_FUND)]);
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
type RuntimeOrigin = <AssetHubZagros as Chain>::RuntimeOrigin;
|
||||
|
||||
let local_fee_asset =
|
||||
Asset { id: AssetId(Location::parent()), fun: Fungible(1_000_000_000_000) };
|
||||
|
||||
let weth_location_reanchored =
|
||||
Location::new(0, [AccountKey20 { network: None, key: WETH.into() }]);
|
||||
|
||||
let weth_asset = Asset {
|
||||
id: AssetId(weth_location_reanchored.clone()),
|
||||
fun: Fungible(TOKEN_AMOUNT * 1_000_000_000),
|
||||
};
|
||||
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::PezkuwiXcm::send(
|
||||
RuntimeOrigin::root(),
|
||||
bx!(VersionedLocation::from(bridge_hub())),
|
||||
bx!(VersionedXcm::from(Xcm(vec![
|
||||
DescendOrigin(sub_location.into()),
|
||||
WithdrawAsset(local_fee_asset.clone().into()),
|
||||
BuyExecution { fees: local_fee_asset.clone(), weight_limit: Unlimited },
|
||||
ExportMessage {
|
||||
network: Ethereum { chain_id: SEPOLIA_ID },
|
||||
destination: Here,
|
||||
xcm: Xcm(vec![
|
||||
WithdrawAsset(weth_asset.clone().into()),
|
||||
DepositAsset { assets: Wild(All), beneficiary: beneficiary() },
|
||||
SetTopic([0; 32]),
|
||||
]),
|
||||
},
|
||||
]))),
|
||||
));
|
||||
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![RuntimeEvent::PezkuwiXcm(pezpallet_xcm::Event::Sent{ .. }) => {},]
|
||||
);
|
||||
});
|
||||
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed{ success:false, .. }) => {},]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn export_from_non_system_teyrchain_will_fail() {
|
||||
let penpal_sovereign = BridgeHubZagros::sovereign_account_id_of(Location::new(
|
||||
1,
|
||||
[Teyrchain(PenpalB::para_id().into())],
|
||||
));
|
||||
BridgeHubZagros::fund_accounts(vec![(penpal_sovereign.clone(), INITIAL_FUND)]);
|
||||
|
||||
PenpalB::execute_with(|| {
|
||||
type RuntimeEvent = <PenpalB as Chain>::RuntimeEvent;
|
||||
type RuntimeOrigin = <PenpalB as Chain>::RuntimeOrigin;
|
||||
|
||||
let local_fee_asset =
|
||||
Asset { id: AssetId(Location::here()), fun: Fungible(1_000_000_000_000) };
|
||||
|
||||
let weth_location_reanchored =
|
||||
Location::new(0, [AccountKey20 { network: None, key: WETH.into() }]);
|
||||
|
||||
let weth_asset =
|
||||
Asset { id: AssetId(weth_location_reanchored.clone()), fun: Fungible(TOKEN_AMOUNT) };
|
||||
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::PezkuwiXcm::send(
|
||||
RuntimeOrigin::root(),
|
||||
bx!(VersionedLocation::from(bridge_hub())),
|
||||
bx!(VersionedXcm::from(Xcm(vec![
|
||||
WithdrawAsset(local_fee_asset.clone().into()),
|
||||
BuyExecution { fees: local_fee_asset.clone(), weight_limit: Unlimited },
|
||||
ExportMessage {
|
||||
network: Ethereum { chain_id: SEPOLIA_ID },
|
||||
destination: Here,
|
||||
xcm: Xcm(vec![
|
||||
WithdrawAsset(weth_asset.clone().into()),
|
||||
DepositAsset { assets: Wild(All), beneficiary: beneficiary() },
|
||||
SetTopic([0; 32]),
|
||||
]),
|
||||
},
|
||||
]))),
|
||||
));
|
||||
|
||||
assert_expected_events!(
|
||||
PenpalB,
|
||||
vec![RuntimeEvent::PezkuwiXcm(pezpallet_xcm::Event::Sent{ .. }) => {},]
|
||||
);
|
||||
});
|
||||
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed{ success:false, origin, .. }) => {
|
||||
origin: *origin == bridge_hub_common::AggregateMessageOrigin::Sibling(PenpalB::para_id()),
|
||||
},]
|
||||
);
|
||||
});
|
||||
}
|
||||
+1068
File diff suppressed because it is too large
Load Diff
+612
@@ -0,0 +1,612 @@
|
||||
// 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::*,
|
||||
tests::{
|
||||
assert_bridge_hub_pezkuwichain_message_received, assert_bridge_hub_zagros_message_accepted,
|
||||
asset_hub_pezkuwichain_location, bridged_roc_at_ah_zagros, create_foreign_on_ah_zagros,
|
||||
snowbridge_common::{
|
||||
asset_hub_zagros_global_location, erc20_token_location, eth_location,
|
||||
register_foreign_asset, register_roc_on_bh, set_up_eth_and_hez_pool,
|
||||
set_up_eth_and_hez_pool_on_pezkuwichain, set_up_pool_with_wnd_on_ah_zagros,
|
||||
snowbridge_sovereign, TOKEN_AMOUNT,
|
||||
},
|
||||
},
|
||||
};
|
||||
use asset_hub_zagros_runtime::ForeignAssets;
|
||||
use bridge_hub_zagros_runtime::{
|
||||
bridge_common_config::BridgeReward, bridge_to_ethereum_config::EthereumGatewayAddress,
|
||||
EthereumInboundQueueV2,
|
||||
};
|
||||
use codec::Encode;
|
||||
use hex_literal::hex;
|
||||
use snowbridge_core::TokenIdOf;
|
||||
use snowbridge_inbound_queue_primitives::v2::{
|
||||
EthereumAsset::{ForeignTokenERC20, NativeTokenERC20},
|
||||
Message, XcmPayload,
|
||||
};
|
||||
use pezsp_core::{H160, H256};
|
||||
use xcm::opaque::latest::AssetTransferFilter::{ReserveDeposit, ReserveWithdraw};
|
||||
use xcm_executor::traits::ConvertLocation;
|
||||
|
||||
/// Calculates the XCM prologue fee for sending an XCM to AH.
|
||||
const INITIAL_FUND: u128 = 500_000_000_000_000;
|
||||
|
||||
/// An ERC-20 token to be registered and sent.
|
||||
const TOKEN_ID: [u8; 20] = hex!("c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2");
|
||||
|
||||
#[test]
|
||||
fn send_token_to_pezkuwichain_v2() {
|
||||
let relayer_account = BridgeHubZagrosSender::get();
|
||||
let relayer_reward = 1_500_000_000_000u128;
|
||||
|
||||
let token: H160 = TOKEN_ID.into();
|
||||
let token_location = erc20_token_location(token);
|
||||
|
||||
let beneficiary_acc_id: H256 = H256::random();
|
||||
let beneficiary_acc_bytes: [u8; 32] = beneficiary_acc_id.into();
|
||||
let beneficiary =
|
||||
Location::new(0, AccountId32 { network: None, id: beneficiary_acc_id.into() });
|
||||
|
||||
let claimer_acc_id = H256::random();
|
||||
let claimer = AccountId32 { network: None, id: claimer_acc_id.into() };
|
||||
let claimer_bytes = claimer.encode();
|
||||
|
||||
// set XCM versions
|
||||
BridgeHubZagros::force_xcm_version(asset_hub_zagros_global_location(), XCM_VERSION);
|
||||
BridgeHubZagros::force_xcm_version(asset_hub_pezkuwichain_location(), XCM_VERSION);
|
||||
AssetHubZagros::force_xcm_version(asset_hub_pezkuwichain_location(), XCM_VERSION);
|
||||
|
||||
// To pay fees on Pezkuwichain.
|
||||
let eth_fee_pezkuwichain_ah: xcm::prelude::Asset =
|
||||
(eth_location(), 3_000_000_000_000u128).into();
|
||||
|
||||
// To satisfy ED
|
||||
AssetHubPezkuwichain::fund_accounts(vec![(
|
||||
pezsp_runtime::AccountId32::from(beneficiary_acc_bytes),
|
||||
3_000_000_000_000,
|
||||
)]);
|
||||
BridgeHubZagros::fund_para_sovereign(AssetHubZagros::para_id(), INITIAL_FUND);
|
||||
|
||||
// Register the token on AH Zagros and Pezkuwichain
|
||||
let snowbridge_sovereign = snowbridge_sovereign();
|
||||
AssetHubPezkuwichain::execute_with(|| {
|
||||
type RuntimeOrigin = <AssetHubPezkuwichain as Chain>::RuntimeOrigin;
|
||||
|
||||
assert_ok!(
|
||||
<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::ForeignAssets::force_create(
|
||||
RuntimeOrigin::root(),
|
||||
token_location.clone().try_into().unwrap(),
|
||||
snowbridge_sovereign.clone().into(),
|
||||
true,
|
||||
1000,
|
||||
)
|
||||
);
|
||||
|
||||
assert!(<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::ForeignAssets::asset_exists(
|
||||
token_location.clone().try_into().unwrap(),
|
||||
));
|
||||
});
|
||||
register_foreign_asset(token_location.clone());
|
||||
|
||||
set_up_eth_and_hez_pool();
|
||||
set_up_eth_and_hez_pool_on_pezkuwichain();
|
||||
|
||||
let token_transfer_value = 2_000_000_000_000u128;
|
||||
|
||||
let assets = vec![
|
||||
// the token being transferred
|
||||
NativeTokenERC20 { token_id: token.into(), value: token_transfer_value },
|
||||
];
|
||||
|
||||
let token_asset_ah: xcm::prelude::Asset = (token_location.clone(), token_transfer_value).into();
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
let instructions = vec![
|
||||
// Send message to Pezkuwichain AH
|
||||
InitiateTransfer {
|
||||
// Pezkuwichain
|
||||
destination: Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(ByGenesis(xcm::latest::PEZKUWICHAIN_GENESIS_HASH)),
|
||||
Teyrchain(1000u32),
|
||||
],
|
||||
),
|
||||
remote_fees: Some(ReserveDeposit(Definite(
|
||||
vec![eth_fee_pezkuwichain_ah.clone()].into(),
|
||||
))),
|
||||
preserve_origin: false,
|
||||
assets: BoundedVec::truncate_from(vec![ReserveDeposit(Definite(
|
||||
vec![token_asset_ah.clone()].into(),
|
||||
))]),
|
||||
remote_xcm: vec![
|
||||
// Refund unspent fees
|
||||
RefundSurplus,
|
||||
// Deposit assets to beneficiary.
|
||||
DepositAsset { assets: Wild(AllCounted(3)), beneficiary: beneficiary.clone() },
|
||||
SetTopic(H256::random().into()),
|
||||
]
|
||||
.into(),
|
||||
},
|
||||
RefundSurplus,
|
||||
DepositAsset {
|
||||
assets: Wild(AllOf { id: AssetId(eth_location()), fun: WildFungibility::Fungible }),
|
||||
beneficiary,
|
||||
},
|
||||
];
|
||||
let xcm: Xcm<()> = instructions.into();
|
||||
let versioned_message_xcm = VersionedXcm::V5(xcm);
|
||||
let origin = EthereumGatewayAddress::get();
|
||||
|
||||
let message = Message {
|
||||
gateway: origin,
|
||||
nonce: 1,
|
||||
origin,
|
||||
assets,
|
||||
xcm: XcmPayload::Raw(versioned_message_xcm.encode()),
|
||||
claimer: Some(claimer_bytes),
|
||||
value: 3_500_000_000_000u128,
|
||||
execution_fee: 1_500_000_000_000u128,
|
||||
relayer_fee: relayer_reward,
|
||||
};
|
||||
|
||||
EthereumInboundQueueV2::process_message(relayer_account.clone(), message).unwrap();
|
||||
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},
|
||||
// Check that the relayer reward was registered.
|
||||
RuntimeEvent::BridgeRelayers(pezpallet_bridge_relayers::Event::RewardRegistered { relayer, reward_kind, reward_balance }) => {
|
||||
relayer: *relayer == relayer_account,
|
||||
reward_kind: *reward_kind == BridgeReward::Snowbridge,
|
||||
reward_balance: *reward_balance == relayer_reward,
|
||||
},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
// Check that the assets were issued on AssetHub
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
// Message processed successfully
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},
|
||||
]
|
||||
);
|
||||
|
||||
let events = AssetHubZagros::events();
|
||||
// Check that no assets were trapped
|
||||
assert!(
|
||||
!events.iter().any(|event| matches!(
|
||||
event,
|
||||
RuntimeEvent::PezkuwiXcm(pezpallet_xcm::Event::AssetsTrapped { .. })
|
||||
)),
|
||||
"Assets were trapped, should not happen."
|
||||
);
|
||||
});
|
||||
|
||||
assert_bridge_hub_zagros_message_accepted(true);
|
||||
|
||||
assert_bridge_hub_pezkuwichain_message_received();
|
||||
|
||||
AssetHubPezkuwichain::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubPezkuwichain as Chain>::RuntimeEvent;
|
||||
|
||||
assert_expected_events!(
|
||||
AssetHubPezkuwichain,
|
||||
vec![
|
||||
// Message processed successfully
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
// Token was issued to beneficiary
|
||||
RuntimeEvent::ForeignAssets(pezpallet_assets::Event::Issued { asset_id, owner, .. }) => {
|
||||
asset_id: *asset_id == token_location,
|
||||
owner: *owner == beneficiary_acc_bytes.into(),
|
||||
},
|
||||
// Leftover fees was deposited to beneficiary
|
||||
RuntimeEvent::ForeignAssets(pezpallet_assets::Event::Issued { asset_id, owner, .. }) => {
|
||||
asset_id: *asset_id == eth_location(),
|
||||
owner: *owner == beneficiary_acc_bytes.into(),
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
// Beneficiary received the token transfer value
|
||||
assert_eq!(
|
||||
ForeignAssets::balance(token_location, AccountId::from(beneficiary_acc_bytes)),
|
||||
token_transfer_value
|
||||
);
|
||||
|
||||
let events = AssetHubPezkuwichain::events();
|
||||
// Check that no assets were trapped
|
||||
assert!(
|
||||
!events.iter().any(|event| matches!(
|
||||
event,
|
||||
RuntimeEvent::PezkuwiXcm(pezpallet_xcm::Event::AssetsTrapped { .. })
|
||||
)),
|
||||
"Assets were trapped on Pezkuwichain AssetHub, should not happen."
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_ether_to_pezkuwichain_v2() {
|
||||
let relayer_account = BridgeHubZagrosSender::get();
|
||||
let relayer_reward = 1_500_000_000_000u128;
|
||||
|
||||
let beneficiary_acc_id: H256 = H256::random();
|
||||
let beneficiary_acc_bytes: [u8; 32] = beneficiary_acc_id.into();
|
||||
let beneficiary =
|
||||
Location::new(0, AccountId32 { network: None, id: beneficiary_acc_id.into() });
|
||||
|
||||
let claimer_acc_id = H256::random();
|
||||
let claimer = AccountId32 { network: None, id: claimer_acc_id.into() };
|
||||
let claimer_bytes = claimer.encode();
|
||||
|
||||
// set XCM versions
|
||||
BridgeHubZagros::force_xcm_version(asset_hub_zagros_global_location(), XCM_VERSION);
|
||||
BridgeHubZagros::force_xcm_version(asset_hub_pezkuwichain_location(), XCM_VERSION);
|
||||
AssetHubZagros::force_xcm_version(asset_hub_pezkuwichain_location(), XCM_VERSION);
|
||||
|
||||
// To pay fees on Pezkuwichain.
|
||||
let eth_fee_pezkuwichain_ah: xcm::prelude::Asset =
|
||||
(eth_location(), 2_000_000_000_000u128).into();
|
||||
let ether_asset_ah: xcm::prelude::Asset = (eth_location(), 4_000_000_000_000u128).into();
|
||||
|
||||
BridgeHubZagros::fund_para_sovereign(AssetHubZagros::para_id(), INITIAL_FUND);
|
||||
|
||||
set_up_eth_and_hez_pool();
|
||||
set_up_eth_and_hez_pool_on_pezkuwichain();
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
let instructions = vec![
|
||||
// Send message to Pezkuwichain AH
|
||||
InitiateTransfer {
|
||||
// Pezkuwichain
|
||||
destination: Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(ByGenesis(xcm::latest::PEZKUWICHAIN_GENESIS_HASH)),
|
||||
Teyrchain(1000u32),
|
||||
],
|
||||
),
|
||||
remote_fees: Some(ReserveDeposit(Definite(
|
||||
vec![eth_fee_pezkuwichain_ah.clone()].into(),
|
||||
))),
|
||||
preserve_origin: false,
|
||||
assets: BoundedVec::truncate_from(vec![ReserveDeposit(Definite(
|
||||
vec![ether_asset_ah.clone()].into(),
|
||||
))]),
|
||||
remote_xcm: vec![
|
||||
// Refund unspent fees
|
||||
RefundSurplus,
|
||||
// Deposit assets to beneficiary.
|
||||
DepositAsset { assets: Wild(AllCounted(3)), beneficiary: beneficiary.clone() },
|
||||
SetTopic(H256::random().into()),
|
||||
]
|
||||
.into(),
|
||||
},
|
||||
RefundSurplus,
|
||||
DepositAsset {
|
||||
assets: Wild(AllOf { id: AssetId(eth_location()), fun: WildFungibility::Fungible }),
|
||||
beneficiary,
|
||||
},
|
||||
];
|
||||
let xcm: Xcm<()> = instructions.into();
|
||||
let versioned_message_xcm = VersionedXcm::V5(xcm);
|
||||
let origin = EthereumGatewayAddress::get();
|
||||
|
||||
let message = Message {
|
||||
gateway: origin,
|
||||
nonce: 1,
|
||||
origin,
|
||||
assets: vec![],
|
||||
xcm: XcmPayload::Raw(versioned_message_xcm.encode()),
|
||||
claimer: Some(claimer_bytes),
|
||||
value: 6_500_000_000_000u128,
|
||||
execution_fee: 1_500_000_000_000u128,
|
||||
relayer_fee: relayer_reward,
|
||||
};
|
||||
|
||||
EthereumInboundQueueV2::process_message(relayer_account.clone(), message).unwrap();
|
||||
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},
|
||||
// Check that the relayer reward was registered.
|
||||
RuntimeEvent::BridgeRelayers(pezpallet_bridge_relayers::Event::RewardRegistered { relayer, reward_kind, reward_balance }) => {
|
||||
relayer: *relayer == relayer_account,
|
||||
reward_kind: *reward_kind == BridgeReward::Snowbridge,
|
||||
reward_balance: *reward_balance == relayer_reward,
|
||||
},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
// Check that the assets were issued on AssetHub
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
// Message processed successfully
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},
|
||||
]
|
||||
);
|
||||
|
||||
let events = AssetHubZagros::events();
|
||||
// Check that no assets were trapped
|
||||
assert!(
|
||||
!events.iter().any(|event| matches!(
|
||||
event,
|
||||
RuntimeEvent::PezkuwiXcm(pezpallet_xcm::Event::AssetsTrapped { .. })
|
||||
)),
|
||||
"Assets were trapped, should not happen."
|
||||
);
|
||||
});
|
||||
|
||||
assert_bridge_hub_zagros_message_accepted(true);
|
||||
|
||||
assert_bridge_hub_pezkuwichain_message_received();
|
||||
|
||||
AssetHubPezkuwichain::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubPezkuwichain as Chain>::RuntimeEvent;
|
||||
|
||||
assert_expected_events!(
|
||||
AssetHubPezkuwichain,
|
||||
vec![
|
||||
// Message processed successfully
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
// Ether was deposited to beneficiary
|
||||
RuntimeEvent::ForeignAssets(pezpallet_assets::Event::Issued { asset_id, owner, .. }) => {
|
||||
asset_id: *asset_id == eth_location(),
|
||||
owner: *owner == beneficiary_acc_bytes.into(),
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
let events = AssetHubPezkuwichain::events();
|
||||
// Check that no assets were trapped
|
||||
assert!(
|
||||
!events.iter().any(|event| matches!(
|
||||
event,
|
||||
RuntimeEvent::PezkuwiXcm(pezpallet_xcm::Event::AssetsTrapped { .. })
|
||||
)),
|
||||
"Assets were trapped on Pezkuwichain AssetHub, should not happen."
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_roc_from_ethereum_to_pezkuwichain() {
|
||||
let initial_fund: u128 = 200_000_000_000_000;
|
||||
let initial_liquidity: u128 = initial_fund / 2;
|
||||
|
||||
let relayer_account = BridgeHubZagrosSender::get();
|
||||
let relayer_reward = 1_500_000_000_000u128;
|
||||
|
||||
let claimer = AccountId32 { network: None, id: H256::random().into() };
|
||||
let claimer_bytes = claimer.encode();
|
||||
let beneficiary = Location::new(
|
||||
0,
|
||||
AccountId32 { network: None, id: AssetHubPezkuwichainReceiver::get().into() },
|
||||
);
|
||||
|
||||
BridgeHubZagros::fund_para_sovereign(AssetHubZagros::para_id(), INITIAL_FUND);
|
||||
|
||||
let ethereum_sovereign: AccountId = snowbridge_sovereign();
|
||||
let bridged_roc_at_asset_hub_zagros = bridged_roc_at_ah_zagros();
|
||||
create_foreign_on_ah_zagros(
|
||||
bridged_roc_at_asset_hub_zagros.clone(),
|
||||
true,
|
||||
vec![(asset_hub_pezkuwichain_location(), false).into()],
|
||||
vec![],
|
||||
);
|
||||
set_up_pool_with_wnd_on_ah_zagros(
|
||||
bridged_roc_at_asset_hub_zagros.clone(),
|
||||
true,
|
||||
initial_fund,
|
||||
initial_liquidity,
|
||||
);
|
||||
|
||||
BridgeHubPezkuwichain::fund_para_sovereign(AssetHubPezkuwichain::para_id(), initial_fund);
|
||||
AssetHubPezkuwichain::fund_accounts(vec![(AssetHubPezkuwichainSender::get(), initial_fund)]);
|
||||
register_roc_on_bh();
|
||||
|
||||
set_up_eth_and_hez_pool();
|
||||
set_up_eth_and_hez_pool_on_pezkuwichain();
|
||||
|
||||
// set XCM versions
|
||||
BridgeHubZagros::force_xcm_version(asset_hub_zagros_global_location(), XCM_VERSION);
|
||||
BridgeHubZagros::force_xcm_version(asset_hub_pezkuwichain_location(), XCM_VERSION);
|
||||
AssetHubZagros::force_xcm_version(asset_hub_pezkuwichain_location(), XCM_VERSION);
|
||||
|
||||
let eth_fee_pezkuwichain_ah: xcm::prelude::Asset =
|
||||
(eth_location(), 2_000_000_000_000u128).into();
|
||||
|
||||
let roc = Location::new(1, [GlobalConsensus(ByGenesis(PEZKUWICHAIN_GENESIS_HASH))]);
|
||||
let token_id = TokenIdOf::convert_location(&roc).unwrap();
|
||||
|
||||
let roc_reachored: xcm::prelude::Asset =
|
||||
(Location::new(2, [GlobalConsensus(ByGenesis(PEZKUWICHAIN_GENESIS_HASH))]), TOKEN_AMOUNT)
|
||||
.into();
|
||||
|
||||
let assets = vec![
|
||||
// the token being transferred
|
||||
ForeignTokenERC20 { token_id: token_id.into(), value: TOKEN_AMOUNT },
|
||||
];
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
// Mint the asset into the bridge sovereign account, to mimic locked funds
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::mint(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(AssetHubZagrosAssetOwner::get()),
|
||||
bridged_roc_at_asset_hub_zagros.clone().into(),
|
||||
ethereum_sovereign.clone().into(),
|
||||
TOKEN_AMOUNT,
|
||||
));
|
||||
});
|
||||
|
||||
// fund the AHW's SA on AHR with the TYR tokens held in reserve
|
||||
let sov_ahw_on_ahr =
|
||||
AssetHubPezkuwichain::sovereign_account_of_teyrchain_on_other_global_consensus(
|
||||
ByGenesis(ZAGROS_GENESIS_HASH),
|
||||
AssetHubZagros::para_id(),
|
||||
);
|
||||
AssetHubPezkuwichain::fund_accounts(vec![(sov_ahw_on_ahr.clone(), INITIAL_FUND)]);
|
||||
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
let instructions = vec![
|
||||
// Send message to Pezkuwichain AH
|
||||
InitiateTransfer {
|
||||
// Pezkuwichain
|
||||
destination: Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(ByGenesis(xcm::latest::PEZKUWICHAIN_GENESIS_HASH)),
|
||||
Teyrchain(1000u32),
|
||||
],
|
||||
),
|
||||
remote_fees: Some(ReserveDeposit(Definite(
|
||||
vec![eth_fee_pezkuwichain_ah.clone()].into(),
|
||||
))),
|
||||
preserve_origin: false,
|
||||
assets: BoundedVec::truncate_from(vec![ReserveWithdraw(Definite(
|
||||
vec![roc_reachored.clone()].into(),
|
||||
))]),
|
||||
remote_xcm: vec![
|
||||
// Refund unspent fees
|
||||
RefundSurplus,
|
||||
// Deposit assets and leftover fees to beneficiary.
|
||||
DepositAsset { assets: Wild(AllCounted(2)), beneficiary: beneficiary.clone() },
|
||||
SetTopic(H256::random().into()),
|
||||
]
|
||||
.into(),
|
||||
},
|
||||
RefundSurplus,
|
||||
DepositAsset {
|
||||
assets: Wild(AllOf { id: AssetId(eth_location()), fun: WildFungibility::Fungible }),
|
||||
beneficiary,
|
||||
},
|
||||
];
|
||||
let xcm: Xcm<()> = instructions.into();
|
||||
let versioned_message_xcm = VersionedXcm::V5(xcm);
|
||||
let origin = EthereumGatewayAddress::get();
|
||||
|
||||
let message = Message {
|
||||
gateway: origin,
|
||||
nonce: 1,
|
||||
origin,
|
||||
assets,
|
||||
xcm: XcmPayload::Raw(versioned_message_xcm.encode()),
|
||||
claimer: Some(claimer_bytes),
|
||||
value: 9_500_000_000_000u128,
|
||||
execution_fee: 3_500_000_000_000u128,
|
||||
relayer_fee: relayer_reward,
|
||||
};
|
||||
|
||||
EthereumInboundQueueV2::process_message(relayer_account.clone(), message).unwrap();
|
||||
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},
|
||||
// Check that the relayer reward was registered.
|
||||
RuntimeEvent::BridgeRelayers(pezpallet_bridge_relayers::Event::RewardRegistered { relayer, reward_kind, reward_balance }) => {
|
||||
relayer: *relayer == relayer_account,
|
||||
reward_kind: *reward_kind == BridgeReward::Snowbridge,
|
||||
reward_balance: *reward_balance == relayer_reward,
|
||||
},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
// Message processed successfully
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
|
||||
let events = AssetHubZagros::events();
|
||||
// Check that no assets were trapped
|
||||
assert!(
|
||||
!events.iter().any(|event| matches!(
|
||||
event,
|
||||
RuntimeEvent::PezkuwiXcm(pezpallet_xcm::Event::AssetsTrapped { .. })
|
||||
)),
|
||||
"Assets were trapped, should not happen."
|
||||
);
|
||||
});
|
||||
|
||||
assert_bridge_hub_zagros_message_accepted(true);
|
||||
|
||||
assert_bridge_hub_pezkuwichain_message_received();
|
||||
|
||||
AssetHubPezkuwichain::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubPezkuwichain as Chain>::RuntimeEvent;
|
||||
|
||||
assert_expected_events!(
|
||||
AssetHubPezkuwichain,
|
||||
vec![
|
||||
// TYR is withdrawn from AHW's SA on AHR
|
||||
RuntimeEvent::Balances(
|
||||
pezpallet_balances::Event::Burned { who, amount }
|
||||
) => {
|
||||
who: *who == sov_ahw_on_ahr,
|
||||
amount: *amount == TOKEN_AMOUNT,
|
||||
},
|
||||
// TYRs deposited to beneficiary
|
||||
RuntimeEvent::Balances(pezpallet_balances::Event::Minted { who, .. }) => {
|
||||
who: *who == AssetHubPezkuwichainReceiver::get(),
|
||||
},
|
||||
// message processed successfully
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
|
||||
let events = AssetHubPezkuwichain::events();
|
||||
// Check that no assets were trapped
|
||||
assert!(
|
||||
!events.iter().any(|event| matches!(
|
||||
event,
|
||||
RuntimeEvent::PezkuwiXcm(pezpallet_xcm::Event::AssetsTrapped { .. })
|
||||
)),
|
||||
"Assets were trapped on Pezkuwichain AssetHub, should not happen."
|
||||
);
|
||||
});
|
||||
}
|
||||
+1042
File diff suppressed because it is too large
Load Diff
+432
@@ -0,0 +1,432 @@
|
||||
// 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::*,
|
||||
tests::{
|
||||
snowbridge_common::*,
|
||||
snowbridge_v2_outbound::{EthereumSystemFrontend, EthereumSystemFrontendCall},
|
||||
usdt_at_ah_zagros,
|
||||
},
|
||||
};
|
||||
use emulated_integration_tests_common::snowbridge::{SEPOLIA_ID, WETH};
|
||||
use pezframe_support::assert_noop;
|
||||
use snowbridge_core::AssetMetadata;
|
||||
use pezsp_runtime::DispatchError::BadOrigin;
|
||||
use xcm::v5::AssetTransferFilter;
|
||||
|
||||
#[test]
|
||||
fn register_penpal_a_asset_from_penpal_b_will_fail() {
|
||||
fund_on_bh();
|
||||
register_assets_on_ah();
|
||||
fund_on_ah();
|
||||
create_pools_on_ah();
|
||||
set_trust_reserve_on_penpal();
|
||||
register_assets_on_penpal();
|
||||
fund_on_penpal();
|
||||
let penpal_user_location = Location::new(
|
||||
1,
|
||||
[
|
||||
Teyrchain(PenpalB::para_id().into()),
|
||||
AccountId32 {
|
||||
network: Some(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
id: PenpalBSender::get().into(),
|
||||
},
|
||||
],
|
||||
);
|
||||
let asset_location_on_penpal =
|
||||
PenpalB::execute_with(|| PenpalLocalTeleportableToAssetHub::get());
|
||||
let penpal_a_asset_at_asset_hub =
|
||||
Location::new(1, [Junction::Teyrchain(PenpalA::para_id().into())])
|
||||
.appended_with(asset_location_on_penpal)
|
||||
.unwrap();
|
||||
PenpalB::execute_with(|| {
|
||||
type RuntimeOrigin = <PenpalB as Chain>::RuntimeOrigin;
|
||||
|
||||
let local_fee_asset_on_penpal =
|
||||
Asset { id: AssetId(Location::parent()), fun: Fungible(LOCAL_FEE_AMOUNT_IN_DOT) };
|
||||
|
||||
let remote_fee_asset_on_ah =
|
||||
Asset { id: AssetId(ethereum()), fun: Fungible(REMOTE_FEE_AMOUNT_IN_ETHER) };
|
||||
|
||||
let remote_fee_asset_on_ethereum =
|
||||
Asset { id: AssetId(ethereum()), fun: Fungible(REMOTE_FEE_AMOUNT_IN_ETHER) };
|
||||
|
||||
let call = EthereumSystemFrontend::EthereumSystemFrontend(
|
||||
EthereumSystemFrontendCall::RegisterToken {
|
||||
asset_id: Box::new(VersionedLocation::from(penpal_a_asset_at_asset_hub)),
|
||||
metadata: Default::default(),
|
||||
fee_asset: remote_fee_asset_on_ethereum.clone(),
|
||||
},
|
||||
);
|
||||
|
||||
let assets = vec![
|
||||
local_fee_asset_on_penpal.clone(),
|
||||
remote_fee_asset_on_ah.clone(),
|
||||
remote_fee_asset_on_ethereum.clone(),
|
||||
];
|
||||
|
||||
let xcm = VersionedXcm::from(Xcm(vec![
|
||||
WithdrawAsset(assets.clone().into()),
|
||||
PayFees { asset: local_fee_asset_on_penpal.clone() },
|
||||
InitiateTransfer {
|
||||
destination: asset_hub(),
|
||||
remote_fees: Some(AssetTransferFilter::ReserveWithdraw(Definite(
|
||||
remote_fee_asset_on_ah.clone().into(),
|
||||
))),
|
||||
preserve_origin: true,
|
||||
assets: BoundedVec::truncate_from(vec![AssetTransferFilter::ReserveWithdraw(
|
||||
Definite(remote_fee_asset_on_ethereum.clone().into()),
|
||||
)]),
|
||||
remote_xcm: Xcm(vec![
|
||||
DepositAsset { assets: Wild(All), beneficiary: penpal_user_location },
|
||||
Transact {
|
||||
origin_kind: OriginKind::Xcm,
|
||||
call: call.encode().into(),
|
||||
fallback_max_weight: None,
|
||||
},
|
||||
]),
|
||||
},
|
||||
]));
|
||||
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::PezkuwiXcm::execute(
|
||||
RuntimeOrigin::root(),
|
||||
bx!(xcm.clone()),
|
||||
Weight::from(EXECUTION_WEIGHT),
|
||||
));
|
||||
});
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![RuntimeEvent::ForeignAssets(pezpallet_assets::Event::Burned { .. }) => {},]
|
||||
);
|
||||
});
|
||||
|
||||
// No events should be emitted on the bridge hub
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
assert_expected_events!(BridgeHubZagros, vec![]);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn export_from_non_system_teyrchain_will_fail() {
|
||||
let penpal_location = Location::new(1, [Teyrchain(PenpalB::para_id().into())]);
|
||||
let penpal_sovereign = BridgeHubZagros::sovereign_account_id_of(penpal_location.clone());
|
||||
BridgeHubZagros::fund_accounts(vec![(penpal_sovereign.clone(), INITIAL_FUND)]);
|
||||
|
||||
PenpalB::execute_with(|| {
|
||||
type RuntimeEvent = <PenpalB as Chain>::RuntimeEvent;
|
||||
type RuntimeOrigin = <PenpalB as Chain>::RuntimeOrigin;
|
||||
|
||||
let relay_fee_asset =
|
||||
Asset { id: AssetId(Location::parent()), fun: Fungible(1_000_000_000_000) };
|
||||
|
||||
let weth_location_reanchored =
|
||||
Location::new(0, [AccountKey20 { network: None, key: WETH.into() }]);
|
||||
|
||||
let weth_asset =
|
||||
Asset { id: AssetId(weth_location_reanchored.clone()), fun: Fungible(TOKEN_AMOUNT) };
|
||||
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::PezkuwiXcm::send(
|
||||
RuntimeOrigin::root(),
|
||||
bx!(VersionedLocation::from(bridge_hub())),
|
||||
bx!(VersionedXcm::from(Xcm(vec![
|
||||
WithdrawAsset(relay_fee_asset.clone().into()),
|
||||
BuyExecution { fees: relay_fee_asset.clone(), weight_limit: Unlimited },
|
||||
ExportMessage {
|
||||
network: Ethereum { chain_id: SEPOLIA_ID },
|
||||
destination: Here,
|
||||
xcm: Xcm(vec![
|
||||
AliasOrigin(penpal_location),
|
||||
WithdrawAsset(weth_asset.clone().into()),
|
||||
DepositAsset { assets: Wild(All), beneficiary: beneficiary() },
|
||||
SetTopic([0; 32]),
|
||||
]),
|
||||
},
|
||||
]))),
|
||||
));
|
||||
|
||||
assert_expected_events!(
|
||||
PenpalB,
|
||||
vec![RuntimeEvent::PezkuwiXcm(pezpallet_xcm::Event::Sent{ .. }) => {},]
|
||||
);
|
||||
});
|
||||
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed{ success: false, .. }) => {},]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn register_usdt_not_from_owner_on_asset_hub_will_fail() {
|
||||
fund_on_bh();
|
||||
register_assets_on_ah();
|
||||
fund_on_ah();
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeOrigin = <AssetHubZagros as Chain>::RuntimeOrigin;
|
||||
|
||||
let fees_asset =
|
||||
Asset { id: AssetId(ethereum()), fun: Fungible(REMOTE_FEE_AMOUNT_IN_ETHER) };
|
||||
|
||||
assert_noop!(
|
||||
<AssetHubZagros as AssetHubZagrosPallet>::SnowbridgeSystemFrontend::register_token(
|
||||
// The owner is Alice, while AssetHubZagrosReceiver is Bob, so it should fail
|
||||
RuntimeOrigin::signed(AssetHubZagrosReceiver::get()),
|
||||
bx!(VersionedLocation::from(usdt_at_ah_zagros())),
|
||||
AssetMetadata {
|
||||
name: "usdt".as_bytes().to_vec().try_into().unwrap(),
|
||||
symbol: "usdt".as_bytes().to_vec().try_into().unwrap(),
|
||||
decimals: 6,
|
||||
},
|
||||
fees_asset
|
||||
),
|
||||
BadOrigin
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn register_relay_token_from_asset_hub_user_origin_will_fail() {
|
||||
fund_on_bh();
|
||||
register_assets_on_ah();
|
||||
fund_on_ah();
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeOrigin = <AssetHubZagros as Chain>::RuntimeOrigin;
|
||||
|
||||
let fees_asset =
|
||||
Asset { id: AssetId(ethereum()), fun: Fungible(REMOTE_FEE_AMOUNT_IN_ETHER) };
|
||||
|
||||
assert_noop!(
|
||||
<AssetHubZagros as AssetHubZagrosPallet>::SnowbridgeSystemFrontend::register_token(
|
||||
RuntimeOrigin::signed(AssetHubZagrosSender::get()),
|
||||
bx!(VersionedLocation::from(Location { parents: 1, interior: [].into() })),
|
||||
AssetMetadata {
|
||||
name: "wnd".as_bytes().to_vec().try_into().unwrap(),
|
||||
symbol: "wnd".as_bytes().to_vec().try_into().unwrap(),
|
||||
decimals: 12,
|
||||
},
|
||||
fees_asset
|
||||
),
|
||||
BadOrigin
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
pub const INSUFFICIENT_REMOTE_FEE_AMOUNT: u128 = 1_000_000_000;
|
||||
|
||||
// Test that the asset trapping and claim flow work correctly.
|
||||
#[test]
|
||||
fn transfer_from_penpal_to_ethereum_trapped_on_ah_and_then_claim_can_work() {
|
||||
create_pools_on_ah();
|
||||
register_pal_on_ah();
|
||||
register_pal_on_bh();
|
||||
fund_on_ah();
|
||||
// penpal
|
||||
set_trust_reserve_on_penpal();
|
||||
register_assets_on_penpal();
|
||||
fund_on_penpal();
|
||||
|
||||
let penpal_user_location = Location::new(
|
||||
1,
|
||||
[
|
||||
Teyrchain(PenpalB::para_id().into()),
|
||||
AccountId32 {
|
||||
network: Some(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
id: PenpalBSender::get().into(),
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
// Since fee is insufficient, asset got trapped on AH
|
||||
PenpalB::execute_with(|| {
|
||||
type RuntimeOrigin = <PenpalB as Chain>::RuntimeOrigin;
|
||||
|
||||
let local_fee_asset_on_penpal =
|
||||
Asset { id: AssetId(Location::parent()), fun: Fungible(LOCAL_FEE_AMOUNT_IN_DOT) };
|
||||
|
||||
let remote_fee_asset_on_ah =
|
||||
Asset { id: AssetId(ethereum()), fun: Fungible(INSUFFICIENT_REMOTE_FEE_AMOUNT) };
|
||||
|
||||
let remote_fee_asset_on_ethereum =
|
||||
Asset { id: AssetId(ethereum()), fun: Fungible(REMOTE_FEE_AMOUNT_IN_ETHER) };
|
||||
|
||||
let ena = Asset { id: AssetId(weth_location()), fun: Fungible(TOKEN_AMOUNT) };
|
||||
|
||||
let assets = vec![
|
||||
local_fee_asset_on_penpal.clone(),
|
||||
remote_fee_asset_on_ah.clone(),
|
||||
remote_fee_asset_on_ethereum.clone(),
|
||||
ena.clone(),
|
||||
];
|
||||
|
||||
let xcm = VersionedXcm::from(Xcm(vec![
|
||||
WithdrawAsset(assets.clone().into()),
|
||||
PayFees { asset: local_fee_asset_on_penpal.clone() },
|
||||
InitiateTransfer {
|
||||
destination: asset_hub(),
|
||||
remote_fees: Some(AssetTransferFilter::ReserveWithdraw(Definite(
|
||||
remote_fee_asset_on_ah.clone().into(),
|
||||
))),
|
||||
preserve_origin: true,
|
||||
assets: BoundedVec::truncate_from(vec![
|
||||
AssetTransferFilter::ReserveWithdraw(Definite(
|
||||
remote_fee_asset_on_ethereum.clone().into(),
|
||||
)),
|
||||
AssetTransferFilter::ReserveWithdraw(Definite(ena.clone().into())),
|
||||
]),
|
||||
remote_xcm: Xcm(vec![
|
||||
SetAppendix(Xcm(vec![SetHints {
|
||||
hints: vec![AssetClaimer { location: penpal_user_location }]
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
}])),
|
||||
InitiateTransfer {
|
||||
destination: ethereum(),
|
||||
remote_fees: Some(AssetTransferFilter::ReserveWithdraw(Definite(
|
||||
remote_fee_asset_on_ethereum.clone().into(),
|
||||
))),
|
||||
preserve_origin: true,
|
||||
assets: BoundedVec::truncate_from(vec![
|
||||
AssetTransferFilter::ReserveWithdraw(Definite(ena.clone().into())),
|
||||
]),
|
||||
remote_xcm: Xcm(vec![DepositAsset {
|
||||
assets: Wild(All),
|
||||
beneficiary: beneficiary(),
|
||||
}]),
|
||||
},
|
||||
]),
|
||||
},
|
||||
]));
|
||||
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::PezkuwiXcm::execute(
|
||||
RuntimeOrigin::signed(PenpalBSender::get()),
|
||||
bx!(xcm.clone()),
|
||||
Weight::from(EXECUTION_WEIGHT),
|
||||
));
|
||||
});
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![RuntimeEvent::PezkuwiXcm(pezpallet_xcm::Event::ProcessXcmError { .. }) => {},]
|
||||
);
|
||||
});
|
||||
|
||||
// Claim the trapped asset and deposit on AH.
|
||||
PenpalB::execute_with(|| {
|
||||
type RuntimeOrigin = <PenpalB as Chain>::RuntimeOrigin;
|
||||
|
||||
let local_fee_asset_on_penpal =
|
||||
Asset { id: AssetId(Location::parent()), fun: Fungible(LOCAL_FEE_AMOUNT_IN_DOT) };
|
||||
|
||||
let remote_fee_asset_on_ah =
|
||||
Asset { id: AssetId(ethereum()), fun: Fungible(REMOTE_FEE_AMOUNT_IN_ETHER) };
|
||||
|
||||
let assets = vec![local_fee_asset_on_penpal.clone(), remote_fee_asset_on_ah.clone()];
|
||||
|
||||
let xcm = VersionedXcm::from(Xcm(vec![
|
||||
WithdrawAsset(assets.clone().into()),
|
||||
PayFees { asset: local_fee_asset_on_penpal.clone() },
|
||||
InitiateTransfer {
|
||||
destination: asset_hub(),
|
||||
remote_fees: Some(AssetTransferFilter::ReserveWithdraw(Definite(
|
||||
remote_fee_asset_on_ah.clone().into(),
|
||||
))),
|
||||
preserve_origin: true,
|
||||
assets: BoundedVec::truncate_from(vec![]),
|
||||
remote_xcm: Xcm(vec![
|
||||
ClaimAsset {
|
||||
assets: vec![
|
||||
Asset { id: AssetId(ethereum()), fun: Fungible(600914043236) },
|
||||
Asset { id: AssetId(weth_location()), fun: Fungible(TOKEN_AMOUNT) },
|
||||
]
|
||||
.into(),
|
||||
ticket: GeneralIndex(5).into(),
|
||||
},
|
||||
RefundSurplus,
|
||||
DepositAsset {
|
||||
assets: Wild(All),
|
||||
beneficiary: AssetHubZagrosReceiver::get().into(),
|
||||
},
|
||||
]),
|
||||
},
|
||||
]));
|
||||
|
||||
assert_ok!(<PenpalB as PenpalBPallet>::PezkuwiXcm::execute(
|
||||
RuntimeOrigin::signed(PenpalBSender::get()),
|
||||
bx!(xcm.clone()),
|
||||
Weight::from(EXECUTION_WEIGHT),
|
||||
));
|
||||
});
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![RuntimeEvent::PezkuwiXcm(pezpallet_xcm::Event::AssetsClaimed { .. }) => {},]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// A malicious user attempted to exploit the bridge by manually adding an AliasOrigin in the
|
||||
// remoteXcm, successfully routing to the V2 path, but ultimately failing at the BH Exporter.
|
||||
#[test]
|
||||
pub fn exploit_v2_route_with_legacy_v1_transfer_will_fail() {
|
||||
create_pools_on_ah();
|
||||
fund_on_ah();
|
||||
|
||||
let remote_fee_asset =
|
||||
Asset { id: AssetId(eth_location()), fun: Fungible(REMOTE_FEE_AMOUNT_IN_ETHER) };
|
||||
|
||||
let reserve_asset = Asset { id: AssetId(eth_location()), fun: Fungible(TOKEN_AMOUNT) };
|
||||
|
||||
let assets = vec![reserve_asset.clone(), remote_fee_asset.clone()];
|
||||
|
||||
let custom_xcm_on_dest = Xcm::<()>(vec![
|
||||
AliasOrigin(Location::parent()),
|
||||
DepositAsset { assets: Wild(AllCounted(2)), beneficiary: beneficiary() },
|
||||
]);
|
||||
|
||||
assert_ok!(AssetHubZagros::execute_with(|| {
|
||||
<AssetHubZagros as AssetHubZagrosPallet>::PezkuwiXcm::transfer_assets_using_type_and_then(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(AssetHubZagrosSender::get()),
|
||||
bx!(eth_location().into()),
|
||||
bx!(assets.into()),
|
||||
bx!(TransferType::DestinationReserve),
|
||||
bx!(AssetId(eth_location()).into()),
|
||||
bx!(TransferType::DestinationReserve),
|
||||
bx!(VersionedXcm::from(custom_xcm_on_dest)),
|
||||
Unlimited,
|
||||
)
|
||||
}));
|
||||
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
// Check that the process failed in MessageQueue
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed{ success: false, .. }) => {},
|
||||
]
|
||||
);
|
||||
})
|
||||
}
|
||||
+372
@@ -0,0 +1,372 @@
|
||||
// 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::*,
|
||||
tests::{
|
||||
asset_hub_pezkuwichain_location, bridged_roc_at_ah_zagros, create_foreign_on_ah_zagros,
|
||||
snowbridge_common::*,
|
||||
snowbridge_v2_outbound::{EthereumSystemFrontend, EthereumSystemFrontendCall},
|
||||
},
|
||||
};
|
||||
use pezframe_support::traits::fungibles::Mutate;
|
||||
use xcm::latest::AssetTransferFilter;
|
||||
|
||||
// set up pool
|
||||
pub(crate) fn set_up_pool_with_wnd_on_ah_zagros(
|
||||
asset: Location,
|
||||
is_foreign: bool,
|
||||
initial_fund: u128,
|
||||
initial_liquidity: u128,
|
||||
) {
|
||||
let wnd: Location = Parent.into();
|
||||
AssetHubZagros::fund_accounts(vec![(AssetHubZagrosSender::get(), initial_fund)]);
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
let owner = AssetHubZagrosSender::get();
|
||||
let signed_owner = <AssetHubZagros as Chain>::RuntimeOrigin::signed(owner.clone());
|
||||
|
||||
if is_foreign {
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::mint(
|
||||
signed_owner.clone(),
|
||||
asset.clone().into(),
|
||||
owner.clone().into(),
|
||||
initial_fund,
|
||||
));
|
||||
} else {
|
||||
let asset_id = match asset.interior.last() {
|
||||
Some(GeneralIndex(id)) => *id as u32,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::Assets::mint(
|
||||
signed_owner.clone(),
|
||||
asset_id.into(),
|
||||
owner.clone().into(),
|
||||
initial_fund,
|
||||
));
|
||||
}
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::AssetConversion::create_pool(
|
||||
signed_owner.clone(),
|
||||
Box::new(wnd.clone()),
|
||||
Box::new(asset.clone()),
|
||||
));
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::AssetConversion(pezpallet_asset_conversion::Event::PoolCreated { .. }) => {},
|
||||
]
|
||||
);
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::AssetConversion::add_liquidity(
|
||||
signed_owner.clone(),
|
||||
Box::new(wnd),
|
||||
Box::new(asset),
|
||||
initial_liquidity,
|
||||
initial_liquidity,
|
||||
1,
|
||||
1,
|
||||
owner.into()
|
||||
));
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::AssetConversion(pezpallet_asset_conversion::Event::LiquidityAdded {..}) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn assert_bridge_hub_pezkuwichain_message_accepted(expected_processed: bool) {
|
||||
BridgeHubPezkuwichain::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubPezkuwichain as Chain>::RuntimeEvent;
|
||||
|
||||
if expected_processed {
|
||||
assert_expected_events!(
|
||||
BridgeHubPezkuwichain,
|
||||
vec![
|
||||
// pay for bridge fees
|
||||
RuntimeEvent::Balances(pezpallet_balances::Event::Burned { .. }) => {},
|
||||
// message exported
|
||||
RuntimeEvent::BridgeZagrosMessages(
|
||||
pezpallet_bridge_messages::Event::MessageAccepted { .. }
|
||||
) => {},
|
||||
// message processed successfully
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
} else {
|
||||
assert_expected_events!(
|
||||
BridgeHubPezkuwichain,
|
||||
vec![
|
||||
RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed {
|
||||
success: false,
|
||||
..
|
||||
}) => {},
|
||||
]
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn assert_bridge_hub_zagros_message_received() {
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![
|
||||
// message sent to destination
|
||||
RuntimeEvent::XcmpQueue(
|
||||
cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_roc_from_asset_hub_pezkuwichain_to_ethereum() {
|
||||
let initial_fund: u128 = 200_000_000_000_000;
|
||||
let initial_liquidity: u128 = initial_fund / 2;
|
||||
let amount: u128 = initial_fund;
|
||||
let roc_fee_amount: u128 = initial_liquidity / 2;
|
||||
let wnd_amount_to_swap: u128 = initial_liquidity / 10;
|
||||
let wnd_fee_amount: u128 = wnd_amount_to_swap / 10;
|
||||
|
||||
let ether_fee_amount: u128 = 4_000_000;
|
||||
|
||||
let sender = AssetHubPezkuwichainSender::get();
|
||||
let roc_at_asset_hub_pezkuwichain = roc_at_ah_pezkuwichain();
|
||||
let bridged_roc_at_asset_hub_zagros = bridged_roc_at_ah_zagros();
|
||||
|
||||
create_foreign_on_ah_zagros(
|
||||
bridged_roc_at_asset_hub_zagros.clone(),
|
||||
true,
|
||||
vec![(asset_hub_pezkuwichain_location(), false).into()],
|
||||
vec![],
|
||||
);
|
||||
set_up_pool_with_wnd_on_ah_zagros(
|
||||
bridged_roc_at_asset_hub_zagros.clone(),
|
||||
true,
|
||||
initial_fund,
|
||||
initial_liquidity,
|
||||
);
|
||||
let previous_owner = snowbridge_sovereign();
|
||||
AssetHubZagros::execute_with(|| {
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::start_destroy(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(previous_owner),
|
||||
ethereum()
|
||||
));
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::finish_destroy(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(AssetHubZagros::account_id_of(ALICE)),
|
||||
ethereum()
|
||||
));
|
||||
});
|
||||
create_foreign_on_ah_zagros(ethereum(), true, vec![(ethereum(), false).into()], vec![]);
|
||||
set_up_pool_with_wnd_on_ah_zagros(ethereum(), true, initial_fund, initial_liquidity);
|
||||
BridgeHubPezkuwichain::fund_para_sovereign(AssetHubPezkuwichain::para_id(), initial_fund);
|
||||
AssetHubPezkuwichain::fund_accounts(vec![(AssetHubPezkuwichainSender::get(), initial_fund)]);
|
||||
fund_on_bh();
|
||||
register_roc_on_bh();
|
||||
|
||||
// set XCM versions
|
||||
AssetHubPezkuwichain::force_xcm_version(asset_hub_zagros_global_location(), XCM_VERSION);
|
||||
BridgeHubPezkuwichain::force_xcm_version(bridge_hub_zagros_location(), XCM_VERSION);
|
||||
|
||||
// send TYRs, use them for fees
|
||||
let local_fee_asset: Asset = (roc_at_asset_hub_pezkuwichain.clone(), roc_fee_amount).into();
|
||||
let remote_fee_on_zagros: Asset =
|
||||
(roc_at_asset_hub_pezkuwichain.clone(), roc_fee_amount).into();
|
||||
let assets: Assets = (roc_at_asset_hub_pezkuwichain.clone(), amount).into();
|
||||
let reserved_asset_on_zagros: Asset =
|
||||
(roc_at_asset_hub_pezkuwichain.clone(), amount - roc_fee_amount * 2).into();
|
||||
let reserved_asset_on_zagros_reanchored: Asset =
|
||||
(bridged_roc_at_asset_hub_zagros.clone(), (amount - roc_fee_amount * 2) / 2).into();
|
||||
|
||||
let xcm = VersionedXcm::from(Xcm(vec![
|
||||
WithdrawAsset(assets.clone().into()),
|
||||
PayFees { asset: local_fee_asset.clone() },
|
||||
InitiateTransfer {
|
||||
destination: asset_hub_zagros_global_location(),
|
||||
remote_fees: Some(AssetTransferFilter::ReserveDeposit(Definite(
|
||||
remote_fee_on_zagros.clone().into(),
|
||||
))),
|
||||
preserve_origin: true,
|
||||
assets: BoundedVec::truncate_from(vec![AssetTransferFilter::ReserveDeposit(Definite(
|
||||
reserved_asset_on_zagros.clone().into(),
|
||||
))]),
|
||||
remote_xcm: Xcm(vec![
|
||||
// swap from roc to wnd
|
||||
ExchangeAsset {
|
||||
give: Definite(reserved_asset_on_zagros_reanchored.clone().into()),
|
||||
want: (Parent, wnd_amount_to_swap).into(),
|
||||
maximal: true,
|
||||
},
|
||||
// swap some wnd to ether
|
||||
ExchangeAsset {
|
||||
give: Definite((Parent, ether_fee_amount * 2).into()),
|
||||
want: (ethereum(), ether_fee_amount).into(),
|
||||
maximal: true,
|
||||
},
|
||||
PayFees { asset: (Parent, wnd_fee_amount).into() },
|
||||
InitiateTransfer {
|
||||
destination: ethereum(),
|
||||
remote_fees: Some(AssetTransferFilter::ReserveWithdraw(Definite(
|
||||
Asset { id: AssetId(ethereum()), fun: Fungible(ether_fee_amount) }.into(),
|
||||
))),
|
||||
preserve_origin: true,
|
||||
assets: BoundedVec::truncate_from(vec![AssetTransferFilter::ReserveDeposit(
|
||||
Definite(reserved_asset_on_zagros_reanchored.clone().into()),
|
||||
)]),
|
||||
remote_xcm: Xcm(vec![DepositAsset {
|
||||
assets: Wild(All),
|
||||
beneficiary: beneficiary(),
|
||||
}]),
|
||||
},
|
||||
]),
|
||||
},
|
||||
]));
|
||||
|
||||
let _ = AssetHubPezkuwichain::execute_with(|| {
|
||||
<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::PezkuwiXcm::execute(
|
||||
<AssetHubPezkuwichain as Chain>::RuntimeOrigin::signed(sender),
|
||||
bx!(xcm),
|
||||
Weight::from(EXECUTION_WEIGHT),
|
||||
)
|
||||
});
|
||||
|
||||
assert_bridge_hub_pezkuwichain_message_accepted(true);
|
||||
assert_bridge_hub_zagros_message_received();
|
||||
|
||||
// verify expected events on final destination
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
// message processed successfully
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
|
||||
// Check that the Ethereum message was queue in the Outbound Queue
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![RuntimeEvent::EthereumOutboundQueueV2(snowbridge_pallet_outbound_queue_v2::Event::MessageQueued{ .. }) => {},]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn register_pezkuwichain_asset_on_ethereum_from_rah() {
|
||||
const XCM_FEE: u128 = 4_000_000_000_000;
|
||||
let sa_of_rah_on_wah = AssetHubZagros::sovereign_account_of_teyrchain_on_other_global_consensus(
|
||||
ByGenesis(PEZKUWICHAIN_GENESIS_HASH),
|
||||
AssetHubPezkuwichain::para_id(),
|
||||
);
|
||||
|
||||
// Pezkuwichain Asset Hub asset when bridged to Zagros Asset Hub.
|
||||
let bridged_asset_at_wah = Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(ByGenesis(PEZKUWICHAIN_GENESIS_HASH)),
|
||||
Teyrchain(AssetHubPezkuwichain::para_id().into()),
|
||||
PalletInstance(ASSETS_PALLET_ID),
|
||||
GeneralIndex(ASSET_ID.into()),
|
||||
],
|
||||
);
|
||||
|
||||
AssetHubZagros::force_create_foreign_asset(
|
||||
bridged_asset_at_wah.clone(),
|
||||
sa_of_rah_on_wah.clone(),
|
||||
true,
|
||||
ASSET_MIN_BALANCE,
|
||||
vec![],
|
||||
);
|
||||
|
||||
let fee_asset = Asset { id: AssetId(ethereum()), fun: Fungible(REMOTE_FEE_AMOUNT_IN_ETHER) };
|
||||
|
||||
let call =
|
||||
EthereumSystemFrontend::EthereumSystemFrontend(EthereumSystemFrontendCall::RegisterToken {
|
||||
asset_id: Box::new(VersionedLocation::from(bridged_asset_at_wah.clone())),
|
||||
metadata: Default::default(),
|
||||
fee_asset,
|
||||
})
|
||||
.encode();
|
||||
|
||||
let origin_kind = OriginKind::Xcm;
|
||||
let fee_amount = XCM_FEE;
|
||||
let fees = (Parent, fee_amount).into();
|
||||
|
||||
let xcm = xcm_transact_paid_execution(call.into(), origin_kind, fees, sa_of_rah_on_wah.clone());
|
||||
|
||||
// SA-of-RAH-on-WAH needs to have balance to pay for fees and asset creation deposit
|
||||
AssetHubZagros::execute_with(|| {
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::ForeignAssets::mint_into(
|
||||
ethereum().try_into().unwrap(),
|
||||
&sa_of_rah_on_wah,
|
||||
INITIAL_FUND,
|
||||
));
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::Balances::force_set_balance(
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::root(),
|
||||
sa_of_rah_on_wah.into(),
|
||||
INITIAL_FUND
|
||||
));
|
||||
});
|
||||
|
||||
let destination = asset_hub_zagros_global_location();
|
||||
|
||||
// fund the RAH's SA on RBH for paying bridge delivery fees
|
||||
BridgeHubPezkuwichain::fund_para_sovereign(
|
||||
AssetHubPezkuwichain::para_id(),
|
||||
10_000_000_000_000u128,
|
||||
);
|
||||
|
||||
// set XCM versions
|
||||
AssetHubPezkuwichain::force_xcm_version(destination.clone(), XCM_VERSION);
|
||||
BridgeHubPezkuwichain::force_xcm_version(bridge_hub_zagros_location(), XCM_VERSION);
|
||||
|
||||
let root_origin = <AssetHubPezkuwichain as Chain>::RuntimeOrigin::root();
|
||||
AssetHubPezkuwichain::execute_with(|| {
|
||||
assert_ok!(<AssetHubPezkuwichain as AssetHubPezkuwichainPallet>::PezkuwiXcm::send(
|
||||
root_origin,
|
||||
bx!(destination.into()),
|
||||
bx!(xcm),
|
||||
));
|
||||
|
||||
AssetHubPezkuwichain::assert_xcm_pallet_sent();
|
||||
});
|
||||
|
||||
assert_bridge_hub_pezkuwichain_message_accepted(true);
|
||||
assert_bridge_hub_zagros_message_received();
|
||||
AssetHubZagros::execute_with(|| {
|
||||
AssetHubZagros::assert_xcmp_queue_success(None);
|
||||
});
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
|
||||
// Check that the Ethereum message was queue in the Outbound Queue
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![RuntimeEvent::EthereumOutboundQueueV2(snowbridge_pallet_outbound_queue_v2::Event::MessageQueued{ .. }) => {},]
|
||||
);
|
||||
});
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
// 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::tests::snowbridge_common::{eth_location, set_up_eth_and_hez_pool};
|
||||
use bridge_hub_zagros_runtime::bridge_common_config::{BridgeReward, BridgeRewardBeneficiaries};
|
||||
use emulated_integration_tests_common::snowbridge::ETHER_MIN_BALANCE;
|
||||
use pezpallet_bridge_relayers::{Error::FailedToPayReward, RewardLedger};
|
||||
|
||||
use crate::imports::*;
|
||||
|
||||
const INITIAL_FUND: u128 = 5_000_000_000_000;
|
||||
//1_000_000_000u128
|
||||
#[test]
|
||||
fn claim_rewards_works() {
|
||||
let assethub_location = BridgeHubZagros::sibling_location_of(AssetHubZagros::para_id());
|
||||
let assethub_sovereign = BridgeHubZagros::sovereign_account_id_of(assethub_location);
|
||||
|
||||
let relayer_account = BridgeHubZagrosSender::get();
|
||||
let reward_address = AssetHubZagrosReceiver::get();
|
||||
|
||||
BridgeHubZagros::fund_accounts(vec![
|
||||
(assethub_sovereign.clone(), INITIAL_FUND),
|
||||
(relayer_account.clone(), INITIAL_FUND),
|
||||
]);
|
||||
set_up_eth_and_hez_pool();
|
||||
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
type RuntimeOrigin = <BridgeHubZagros as Chain>::RuntimeOrigin;
|
||||
let reward_amount = ETHER_MIN_BALANCE * 2; // Reward should be more than Ether min balance
|
||||
|
||||
type BridgeRelayers = <BridgeHubZagros as BridgeHubZagrosPallet>::BridgeRelayers;
|
||||
BridgeRelayers::register_reward(
|
||||
(&relayer_account.clone()).into(),
|
||||
BridgeReward::Snowbridge,
|
||||
reward_amount,
|
||||
);
|
||||
|
||||
// Check that the reward was registered.
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::BridgeRelayers(pezpallet_bridge_relayers::Event::RewardRegistered { relayer, reward_kind, reward_balance }) => {
|
||||
relayer: *relayer == relayer_account,
|
||||
reward_kind: *reward_kind == BridgeReward::Snowbridge,
|
||||
reward_balance: *reward_balance == reward_amount,
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
let relayer_location = Location::new(
|
||||
0,
|
||||
[Junction::AccountId32 { id: reward_address.clone().into(), network: None }],
|
||||
);
|
||||
let reward_beneficiary =
|
||||
BridgeRewardBeneficiaries::AssetHubLocation(VersionedLocation::V5(relayer_location));
|
||||
let result = BridgeRelayers::claim_rewards_to(
|
||||
RuntimeOrigin::signed(relayer_account.clone()),
|
||||
BridgeReward::Snowbridge,
|
||||
reward_beneficiary.clone(),
|
||||
);
|
||||
assert_ok!(result);
|
||||
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![
|
||||
// Check that the pay reward event was emitted on BH
|
||||
RuntimeEvent::BridgeRelayers(pezpallet_bridge_relayers::Event::RewardPaid { relayer, reward_kind, reward_balance, beneficiary }) => {
|
||||
relayer: *relayer == relayer_account,
|
||||
reward_kind: *reward_kind == BridgeReward::Snowbridge,
|
||||
reward_balance: *reward_balance == reward_amount,
|
||||
beneficiary: *beneficiary == reward_beneficiary,
|
||||
},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
// Check that the reward was paid on AH
|
||||
RuntimeEvent::ForeignAssets(pezpallet_assets::Event::Issued { asset_id, owner, .. }) => {
|
||||
asset_id: *asset_id == eth_location(),
|
||||
owner: *owner == reward_address.clone().into(),
|
||||
},
|
||||
]
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn claim_snowbridge_rewards_to_local_account_fails() {
|
||||
let assethub_location = BridgeHubZagros::sibling_location_of(AssetHubZagros::para_id());
|
||||
let assethub_sovereign = BridgeHubZagros::sovereign_account_id_of(assethub_location);
|
||||
|
||||
let relayer_account = BridgeHubZagrosSender::get();
|
||||
let reward_address = AssetHubZagrosReceiver::get();
|
||||
|
||||
BridgeHubZagros::fund_accounts(vec![
|
||||
(assethub_sovereign.clone(), INITIAL_FUND),
|
||||
(relayer_account.clone(), INITIAL_FUND),
|
||||
]);
|
||||
set_up_eth_and_hez_pool();
|
||||
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
type Runtime = <BridgeHubZagros as Chain>::Runtime;
|
||||
type RuntimeEvent = <BridgeHubZagros as Chain>::RuntimeEvent;
|
||||
type RuntimeOrigin = <BridgeHubZagros as Chain>::RuntimeOrigin;
|
||||
let reward_amount = ETHER_MIN_BALANCE * 2; // Reward should be more than Ether min balance
|
||||
|
||||
type BridgeRelayers = <BridgeHubZagros as BridgeHubZagrosPallet>::BridgeRelayers;
|
||||
BridgeRelayers::register_reward(
|
||||
&relayer_account.clone(),
|
||||
BridgeReward::Snowbridge,
|
||||
reward_amount,
|
||||
);
|
||||
|
||||
// Check that the reward was registered.
|
||||
assert_expected_events!(
|
||||
BridgeHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::BridgeRelayers(pezpallet_bridge_relayers::Event::RewardRegistered { relayer, reward_kind, reward_balance }) => {
|
||||
relayer: *relayer == relayer_account,
|
||||
reward_kind: *reward_kind == BridgeReward::Snowbridge,
|
||||
reward_balance: *reward_balance == reward_amount,
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
let reward_beneficiary = BridgeRewardBeneficiaries::LocalAccount(reward_address);
|
||||
let result = BridgeRelayers::claim_rewards_to(
|
||||
RuntimeOrigin::signed(relayer_account.clone()),
|
||||
BridgeReward::Snowbridge,
|
||||
reward_beneficiary.clone(),
|
||||
);
|
||||
assert_err!(result, FailedToPayReward::<Runtime, ()>);
|
||||
})
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
// 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 teleport_via_limited_teleport_assets_to_other_system_teyrchains_works() {
|
||||
let amount = BRIDGE_HUB_ZAGROS_ED * 100;
|
||||
let native_asset: Assets = (Parent, amount).into();
|
||||
|
||||
let fee_asset_id: AssetId = Parent.into();
|
||||
test_teyrchain_is_trusted_teleporter!(
|
||||
BridgeHubZagros, // Origin
|
||||
vec![AssetHubZagros], // Destinations
|
||||
(native_asset, amount),
|
||||
fee_asset_id,
|
||||
limited_teleport_assets
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleport_via_transfer_assets_to_other_system_teyrchains_works() {
|
||||
let amount = BRIDGE_HUB_ZAGROS_ED * 100;
|
||||
let native_asset: Assets = (Parent, amount).into();
|
||||
|
||||
let fee_asset_id: AssetId = Parent.into();
|
||||
test_teyrchain_is_trusted_teleporter!(
|
||||
BridgeHubZagros, // Origin
|
||||
vec![AssetHubZagros], // Destinations
|
||||
(native_asset, amount),
|
||||
fee_asset_id,
|
||||
transfer_assets
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleport_via_limited_teleport_assets_from_and_to_relay() {
|
||||
let amount = ZAGROS_ED * 100;
|
||||
|
||||
test_relay_is_trusted_teleporter!(
|
||||
Zagros,
|
||||
vec![BridgeHubZagros],
|
||||
amount,
|
||||
limited_teleport_assets
|
||||
);
|
||||
|
||||
test_teyrchain_is_trusted_teleporter_for_relay!(
|
||||
BridgeHubZagros,
|
||||
Zagros,
|
||||
amount,
|
||||
limited_teleport_assets
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleport_via_transfer_assets_from_and_to_relay() {
|
||||
let amount = ZAGROS_ED * 100;
|
||||
|
||||
test_relay_is_trusted_teleporter!(Zagros, vec![BridgeHubZagros], amount, transfer_assets);
|
||||
|
||||
test_teyrchain_is_trusted_teleporter_for_relay!(
|
||||
BridgeHubZagros,
|
||||
Zagros,
|
||||
amount,
|
||||
transfer_assets
|
||||
);
|
||||
}
|
||||
+248
@@ -0,0 +1,248 @@
|
||||
// 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::tests::{snowbridge_common::snowbridge_sovereign, *};
|
||||
use pezsp_core::Get;
|
||||
use xcm::latest::AssetTransferFilter;
|
||||
|
||||
const ETHEREUM_BOB: [u8; 20] = hex_literal::hex!("11b0b11000011b0b11000011b0b11000011b0b11");
|
||||
|
||||
/// Bob on Ethereum transacts on PenpalB, paying fees using WETH. XCM has to go through Asset Hub
|
||||
/// as the reserve location of WETH. The original origin `Ethereum/Bob` is proxied by Asset Hub.
|
||||
///
|
||||
/// This particular test is not testing snowbridge, but only Bridge Hub, so the tested XCM flow from
|
||||
/// Ethereum starts from Bridge Hub.
|
||||
// TODO(https://github.com/pezkuwichain/pezkuwi-sdk/issues/149): Once Snowbridge supports Transact, start the flow from Ethereum and test completely e2e.
|
||||
fn transfer_and_transact_in_same_xcm(
|
||||
sender: Location,
|
||||
weth: Asset,
|
||||
destination: Location,
|
||||
beneficiary: Location,
|
||||
call: xcm::DoubleEncoded<()>,
|
||||
) {
|
||||
let signed_origin = <BridgeHubZagros as Chain>::RuntimeOrigin::root();
|
||||
let context: InteriorLocation = [
|
||||
GlobalConsensus(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
Teyrchain(<BridgeHubZagros as Para>::TeyrchainInfo::get().into()),
|
||||
]
|
||||
.into();
|
||||
let asset_hub_location = BridgeHubZagros::sibling_location_of(AssetHubZagros::para_id());
|
||||
|
||||
// TODO(https://github.com/pezkuwichain/pezkuwi-sdk/issues/147): dry-run to get local fees, for now use hardcoded value.
|
||||
let ah_fees_amount = 90_000_000_000u128; // current exact value 79_948_099_299
|
||||
let fees_for_ah: Asset = (weth.id.clone(), ah_fees_amount).into();
|
||||
|
||||
// xcm to be executed at dest
|
||||
let xcm_on_dest = Xcm(vec![
|
||||
Transact { origin_kind: OriginKind::Xcm, call, fallback_max_weight: None },
|
||||
ExpectTransactStatus(MaybeErrorCode::Success),
|
||||
// since this is the last hop, we don't need to further use any assets previously
|
||||
// reserved for fees (there are no further hops to cover delivery fees for); we
|
||||
// RefundSurplus to get back any unspent fees
|
||||
RefundSurplus,
|
||||
DepositAsset { assets: Wild(All), beneficiary },
|
||||
]);
|
||||
let destination = destination.reanchored(&asset_hub_location, &context).unwrap();
|
||||
let xcm_to_ah = Xcm::<()>(vec![
|
||||
UnpaidExecution { check_origin: None, weight_limit: Unlimited },
|
||||
DescendOrigin([PalletInstance(80)].into()), // snowbridge pallet
|
||||
UniversalOrigin(GlobalConsensus(Ethereum { chain_id: SEPOLIA_ID })),
|
||||
ReserveAssetDeposited(weth.clone().into()),
|
||||
AliasOrigin(sender),
|
||||
PayFees { asset: fees_for_ah },
|
||||
InitiateTransfer {
|
||||
destination,
|
||||
// on the last hop we can just put everything in fees and `RefundSurplus` to get any
|
||||
// unused back
|
||||
remote_fees: Some(AssetTransferFilter::ReserveDeposit(Wild(All))),
|
||||
preserve_origin: true,
|
||||
assets: BoundedVec::new(),
|
||||
remote_xcm: xcm_on_dest,
|
||||
},
|
||||
]);
|
||||
<BridgeHubZagros as BridgeHubZagrosPallet>::PezkuwiXcm::send(
|
||||
signed_origin,
|
||||
bx!(asset_hub_location.into()),
|
||||
bx!(xcm::VersionedXcm::from(xcm_to_ah.into())),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// Bob on Ethereum transacts on PenpalB, paying fees using WETH. XCM has to go through Asset Hub
|
||||
/// as the reserve location of WETH. The original origin `Ethereum/Bob` is proxied by Asset Hub.
|
||||
///
|
||||
/// This particular test is not testing snowbridge, but only Bridge Hub, so the tested XCM flow from
|
||||
/// Ethereum starts from Bridge Hub.
|
||||
// TODO(https://github.com/pezkuwichain/pezkuwi-sdk/issues/149): Once Snowbridge supports Transact, start the flow from Ethereum and test completely e2e.
|
||||
#[test]
|
||||
fn transact_from_ethereum_to_penpalb_through_asset_hub() {
|
||||
// Snowbridge doesn't support transact yet, we are emulating it by sending one from Bridge Hub
|
||||
// as if it comes from Snowbridge.
|
||||
let destination = BridgeHubZagros::sibling_location_of(PenpalB::para_id());
|
||||
let sender = Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(Ethereum { chain_id: SEPOLIA_ID }),
|
||||
AccountKey20 { network: None, key: ETHEREUM_BOB },
|
||||
],
|
||||
);
|
||||
|
||||
let bridged_weth = weth_at_asset_hubs();
|
||||
PenpalB::force_create_foreign_asset(
|
||||
bridged_weth.clone(),
|
||||
PenpalAssetOwner::get(),
|
||||
true,
|
||||
ASSET_MIN_BALANCE,
|
||||
vec![],
|
||||
);
|
||||
// Configure source Penpal chain to trust local AH as reserve of bridged WETH
|
||||
PenpalB::execute_with(|| {
|
||||
assert_ok!(<PenpalB as Chain>::System::set_storage(
|
||||
<PenpalB as Chain>::RuntimeOrigin::root(),
|
||||
vec![(
|
||||
PenpalCustomizableAssetFromSystemAssetHub::key().to_vec(),
|
||||
bridged_weth.encode(),
|
||||
)],
|
||||
));
|
||||
});
|
||||
|
||||
let fee_amount_to_send: teyrchains_common::Balance = ASSET_HUB_ZAGROS_ED * 10000;
|
||||
let sender_chain_as_seen_by_asset_hub =
|
||||
Location::new(2, [GlobalConsensus(Ethereum { chain_id: SEPOLIA_ID })]);
|
||||
|
||||
let sov_of_sender_on_asset_hub = AssetHubZagros::execute_with(|| {
|
||||
AssetHubZagros::sovereign_account_id_of(sender_chain_as_seen_by_asset_hub)
|
||||
});
|
||||
let receiver_as_seen_by_asset_hub = AssetHubZagros::sibling_location_of(PenpalB::para_id());
|
||||
let sov_of_receiver_on_asset_hub = AssetHubZagros::execute_with(|| {
|
||||
AssetHubZagros::sovereign_account_id_of(receiver_as_seen_by_asset_hub)
|
||||
});
|
||||
// Create SAs of sender and receiver on AHW with ED.
|
||||
AssetHubZagros::fund_accounts(vec![
|
||||
(sov_of_sender_on_asset_hub.clone().into(), ASSET_HUB_ZAGROS_ED),
|
||||
(sov_of_receiver_on_asset_hub.clone().into(), ASSET_HUB_ZAGROS_ED),
|
||||
(snowbridge_sovereign().into(), 10_000_000_000_00),
|
||||
]);
|
||||
|
||||
// We create a pool between ZGR and WETH in AssetHub to support paying for fees with WETH.
|
||||
let snowbridge_sovereign = snowbridge_sovereign();
|
||||
create_pool_with_native_on!(
|
||||
AssetHubZagros,
|
||||
bridged_weth.clone(),
|
||||
true,
|
||||
snowbridge_sovereign,
|
||||
1_000_000_000_000,
|
||||
20_000_000_000
|
||||
);
|
||||
// We also need a pool between ZGR and WETH on PenpalB to support paying for fees with WETH.
|
||||
create_pool_with_native_on!(
|
||||
PenpalB,
|
||||
bridged_weth.clone(),
|
||||
true,
|
||||
PenpalAssetOwner::get(),
|
||||
1_000_000_000_000,
|
||||
20_000_000_000
|
||||
);
|
||||
|
||||
// Init values for Teyrchain Destination
|
||||
let receiver = PenpalBReceiver::get();
|
||||
|
||||
// Query initial balances
|
||||
let receiver_assets_before = PenpalB::execute_with(|| {
|
||||
type Assets = <PenpalB as PenpalBPallet>::ForeignAssets;
|
||||
<Assets as Inspect<_>>::balance(bridged_weth.clone(), &receiver)
|
||||
});
|
||||
|
||||
// Now register a new asset on PenpalB from Ethereum/Bob account while paying fees using WETH
|
||||
// (going through Asset Hub)
|
||||
let weth_to_send: Asset = (bridged_weth.clone(), fee_amount_to_send).into();
|
||||
// Silly example of a Transact: Bob creates his own foreign assset on PenpalB based on his
|
||||
// Ethereum address
|
||||
let foreign_asset_at_penpal_b = Location::new(
|
||||
2,
|
||||
[
|
||||
GlobalConsensus(Ethereum { chain_id: SEPOLIA_ID }),
|
||||
AccountKey20 { network: None, key: ETHEREUM_BOB },
|
||||
],
|
||||
);
|
||||
// Encoded `create_asset` call to be executed in PenpalB
|
||||
let call = PenpalB::create_foreign_asset_call(
|
||||
foreign_asset_at_penpal_b.clone(),
|
||||
ASSET_MIN_BALANCE,
|
||||
receiver.clone(),
|
||||
);
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
// initiate transaction
|
||||
transfer_and_transact_in_same_xcm(
|
||||
sender.clone(),
|
||||
weth_to_send,
|
||||
destination,
|
||||
receiver.clone().into(),
|
||||
call,
|
||||
);
|
||||
});
|
||||
AssetHubZagros::execute_with(|| {
|
||||
asset_hub_hop_assertions();
|
||||
});
|
||||
PenpalB::execute_with(|| {
|
||||
let expected_creator = PenpalB::sovereign_account_id_of(sender);
|
||||
penpal_b_assertions(foreign_asset_at_penpal_b, expected_creator, receiver.clone());
|
||||
});
|
||||
|
||||
// Query final balances
|
||||
let receiver_assets_after = PenpalB::execute_with(|| {
|
||||
type Assets = <PenpalB as PenpalBPallet>::ForeignAssets;
|
||||
<Assets as Inspect<_>>::balance(bridged_weth, &receiver)
|
||||
});
|
||||
// Receiver's balance is increased
|
||||
assert!(receiver_assets_after > receiver_assets_before);
|
||||
}
|
||||
|
||||
fn asset_hub_hop_assertions() {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
// Deposited to receiver teyrchain SA
|
||||
RuntimeEvent::ForeignAssets(
|
||||
pezpallet_assets::Event::Deposited { .. }
|
||||
) => {},
|
||||
RuntimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
fn penpal_b_assertions(
|
||||
expected_asset: Location,
|
||||
expected_creator: AccountId,
|
||||
expected_owner: AccountId,
|
||||
) {
|
||||
type RuntimeEvent = <PenpalB as Chain>::RuntimeEvent;
|
||||
PenpalB::assert_xcmp_queue_success(None);
|
||||
assert_expected_events!(
|
||||
PenpalB,
|
||||
vec![
|
||||
RuntimeEvent::ForeignAssets(
|
||||
pezpallet_assets::Event::Created { asset_id, creator, owner }
|
||||
) => {
|
||||
asset_id: *asset_id == expected_asset,
|
||||
creator: *creator == expected_creator,
|
||||
owner: *owner == expected_owner,
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
[package]
|
||||
name = "collectives-zagros-integration-tests"
|
||||
version = "1.0.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license = "Apache-2.0"
|
||||
description = "Collectives Zagros runtime integration tests with xcm-emulator"
|
||||
publish = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
codec = { workspace = true }
|
||||
|
||||
# Bizinikiwi
|
||||
pezframe-support = { workspace = true }
|
||||
pezpallet-assets = { workspace = true }
|
||||
pezpallet-balances = { workspace = true }
|
||||
pezpallet-message-queue = { workspace = true }
|
||||
pezpallet-treasury = { workspace = true }
|
||||
pezpallet-utility = { workspace = true }
|
||||
pezpallet-whitelist = { workspace = true }
|
||||
pezsp-runtime = { workspace = true }
|
||||
|
||||
# Pezkuwi
|
||||
collectives-zagros-runtime = { workspace = true }
|
||||
pezpallet-xcm = { workspace = true }
|
||||
pezkuwi-runtime-common = { workspace = true, default-features = true }
|
||||
xcm = { workspace = true }
|
||||
xcm-executor = { workspace = true }
|
||||
zagros-runtime-constants = { workspace = true, default-features = true }
|
||||
|
||||
# Pezcumulus
|
||||
pezcumulus-pezpallet-teyrchain-system = { workspace = true }
|
||||
pezcumulus-pezpallet-xcmp-queue = { workspace = true }
|
||||
emulated-integration-tests-common = { workspace = true }
|
||||
zagros-system-emulated-network = { workspace = true }
|
||||
|
||||
[features]
|
||||
runtime-benchmarks = [
|
||||
"collectives-zagros-runtime/runtime-benchmarks",
|
||||
"pezcumulus-pezpallet-teyrchain-system/runtime-benchmarks",
|
||||
"pezcumulus-pezpallet-xcmp-queue/runtime-benchmarks",
|
||||
"emulated-integration-tests-common/runtime-benchmarks",
|
||||
"pezframe-support/runtime-benchmarks",
|
||||
"pezpallet-assets/runtime-benchmarks",
|
||||
"pezpallet-balances/runtime-benchmarks",
|
||||
"pezpallet-message-queue/runtime-benchmarks",
|
||||
"pezpallet-treasury/runtime-benchmarks",
|
||||
"pezpallet-utility/runtime-benchmarks",
|
||||
"pezpallet-whitelist/runtime-benchmarks",
|
||||
"pezpallet-xcm/runtime-benchmarks",
|
||||
"pezkuwi-runtime-common/runtime-benchmarks",
|
||||
"pezsp-runtime/runtime-benchmarks",
|
||||
"xcm-executor/runtime-benchmarks",
|
||||
"xcm/runtime-benchmarks",
|
||||
"zagros-runtime-constants/runtime-benchmarks",
|
||||
"zagros-system-emulated-network/runtime-benchmarks",
|
||||
]
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
// 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 xcm::{latest::ZAGROS_GENESIS_HASH, prelude::*};
|
||||
|
||||
pub(crate) use pezframe_support::assert_ok;
|
||||
|
||||
pub(crate) use emulated_integration_tests_common::{
|
||||
accounts::ALICE,
|
||||
test_teyrchain_is_trusted_teleporter,
|
||||
xcm_emulator::{assert_expected_events, bx, Chain, TestExt, Teyrchain},
|
||||
};
|
||||
pub(crate) use zagros_system_emulated_network::{
|
||||
asset_hub_zagros_emulated_chain::{
|
||||
asset_hub_zagros_runtime::xcm_config::LocationToAccountId as AssetHubLocationToAccountId,
|
||||
genesis::ED as ASSET_HUB_ZAGROS_ED, AssetHubZagrosParaPallet as AssetHubZagrosPallet,
|
||||
},
|
||||
bridge_hub_zagros_emulated_chain::BridgeHubZagrosParaPallet as BridgeHubZagrosPallet,
|
||||
collectives_zagros_emulated_chain::{
|
||||
collectives_zagros_runtime::{
|
||||
fellowship as collectives_fellowship,
|
||||
xcm_config::XcmConfig as CollectivesZagrosXcmConfig,
|
||||
},
|
||||
genesis::ED as COLLECTIVES_ZAGROS_ED,
|
||||
CollectivesZagrosParaPallet as CollectivesZagrosPallet,
|
||||
},
|
||||
coretime_zagros_emulated_chain::CoretimeZagrosParaPallet as CoretimeZagrosPallet,
|
||||
penpal_emulated_chain::{PenpalAssetOwner, PenpalBParaPallet as PenpalBPallet},
|
||||
people_zagros_emulated_chain::PeopleZagrosParaPallet as PeopleZagrosPallet,
|
||||
zagros_emulated_chain::{
|
||||
genesis::ED as ZAGROS_ED,
|
||||
zagros_runtime::{governance as zagros_governance, OriginCaller as ZagrosOriginCaller},
|
||||
ZagrosRelayPallet as ZagrosPallet,
|
||||
},
|
||||
AssetHubZagrosPara as AssetHubZagros, AssetHubZagrosParaReceiver as AssetHubZagrosReceiver,
|
||||
AssetHubZagrosParaSender as AssetHubZagrosSender, BridgeHubZagrosPara as BridgeHubZagros,
|
||||
CollectivesZagrosPara as CollectivesZagros,
|
||||
CollectivesZagrosParaReceiver as CollectivesZagrosReceiver,
|
||||
CollectivesZagrosParaSender as CollectivesZagrosSender,
|
||||
CoretimeZagrosPara as CoretimeZagros, PenpalBPara as PenpalB,
|
||||
PeopleZagrosPara as PeopleZagros, ZagrosRelay as Zagros,
|
||||
ZagrosRelayReceiver as ZagrosReceiver, ZagrosRelaySender as ZagrosSender,
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
+275
@@ -0,0 +1,275 @@
|
||||
// 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 XCM aliasing.
|
||||
|
||||
use crate::imports::*;
|
||||
use emulated_integration_tests_common::{macros::AccountId, test_cross_chain_alias};
|
||||
use pezframe_support::traits::ContainsPair;
|
||||
use xcm::latest::Junctions::*;
|
||||
|
||||
const ALLOWED: bool = true;
|
||||
const DENIED: bool = false;
|
||||
|
||||
const TELEPORT_FEES: bool = true;
|
||||
const RESERVE_TRANSFER_FEES: bool = false;
|
||||
|
||||
#[test]
|
||||
fn account_on_sibling_syschain_aliases_into_same_local_account() {
|
||||
// origin and target are the same account on different chains
|
||||
let origin: AccountId = [1; 32].into();
|
||||
let target = origin.clone();
|
||||
let fees = ZAGROS_ED * 10;
|
||||
|
||||
PenpalB::mint_foreign_asset(
|
||||
<PenpalB as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get()),
|
||||
Location::parent(),
|
||||
origin.clone(),
|
||||
fees * 10,
|
||||
);
|
||||
|
||||
// Aliasing same account on different chains
|
||||
test_cross_chain_alias!(
|
||||
vec![
|
||||
// between AH and Collectives: allowed
|
||||
(AssetHubZagros, CollectivesZagros, TELEPORT_FEES, ALLOWED),
|
||||
// between BH and Collectives: allowed
|
||||
(BridgeHubZagros, CollectivesZagros, TELEPORT_FEES, ALLOWED),
|
||||
// between Coretime and Collectives: allowed
|
||||
(CoretimeZagros, CollectivesZagros, TELEPORT_FEES, ALLOWED),
|
||||
// between People and Collectives: allowed
|
||||
(PeopleZagros, CollectivesZagros, TELEPORT_FEES, ALLOWED),
|
||||
// between Penpal and Collectives: denied
|
||||
(PenpalB, CollectivesZagros, RESERVE_TRANSFER_FEES, DENIED)
|
||||
],
|
||||
origin,
|
||||
target,
|
||||
fees
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn account_on_sibling_syschain_cannot_alias_into_different_local_account() {
|
||||
// origin and target are different accounts on different chains
|
||||
let origin: AccountId = [1; 32].into();
|
||||
let target: AccountId = [2; 32].into();
|
||||
let fees = ZAGROS_ED * 10;
|
||||
|
||||
PenpalB::mint_foreign_asset(
|
||||
<PenpalB as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get()),
|
||||
Location::parent(),
|
||||
origin.clone(),
|
||||
fees * 10,
|
||||
);
|
||||
|
||||
// Aliasing different account on different chains
|
||||
test_cross_chain_alias!(
|
||||
vec![
|
||||
// between AH and Collectives: denied
|
||||
(AssetHubZagros, CollectivesZagros, TELEPORT_FEES, DENIED),
|
||||
// between BH and Collectives: denied
|
||||
(BridgeHubZagros, CollectivesZagros, TELEPORT_FEES, DENIED),
|
||||
// between Coretime and Collectives: denied
|
||||
(CoretimeZagros, CollectivesZagros, TELEPORT_FEES, DENIED),
|
||||
// between People and Collectives: denied
|
||||
(PeopleZagros, CollectivesZagros, TELEPORT_FEES, DENIED),
|
||||
// between Penpal and Collectives: denied
|
||||
(PenpalB, CollectivesZagros, RESERVE_TRANSFER_FEES, DENIED)
|
||||
],
|
||||
origin,
|
||||
target,
|
||||
fees
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aliasing_child_locations() {
|
||||
use CollectivesZagrosXcmConfig as XcmConfig;
|
||||
CollectivesZagros::execute_with(|| {
|
||||
// Allows aliasing descendant of origin.
|
||||
let origin = Location::new(1, X1([PalletInstance(8)].into()));
|
||||
let target = Location::new(1, X2([PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(8)].into()));
|
||||
let target = Location::new(
|
||||
1,
|
||||
X2([Teyrchain(8), AccountId32 { network: None, id: [1u8; 32] }].into()),
|
||||
);
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(8)].into()));
|
||||
let target =
|
||||
Location::new(1, X3([Teyrchain(8), PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
|
||||
// Does not allow if not descendant.
|
||||
let origin = Location::new(1, X1([PalletInstance(8)].into()));
|
||||
let target = Location::new(0, X2([PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(8)].into()));
|
||||
let target = Location::new(
|
||||
0,
|
||||
X2([Teyrchain(8), AccountId32 { network: None, id: [1u8; 32] }].into()),
|
||||
);
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(8)].into()));
|
||||
let target = Location::new(0, X1([AccountId32 { network: None, id: [1u8; 32] }].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([AccountId32 { network: None, id: [1u8; 32] }].into()));
|
||||
let target = Location::new(0, X1([AccountId32 { network: None, id: [1u8; 32] }].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn asset_hub_root_aliases_anything() {
|
||||
use CollectivesZagrosXcmConfig as XcmConfig;
|
||||
CollectivesZagros::execute_with(|| {
|
||||
// Allows AH root to alias anything.
|
||||
let origin = Location::new(1, X1([Teyrchain(1000)].into()));
|
||||
|
||||
let target = Location::new(1, X1([Teyrchain(2000)].into()));
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(1, X1([AccountId32 { network: None, id: [1u8; 32] }].into()));
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(
|
||||
1,
|
||||
X2([Teyrchain(8), AccountId32 { network: None, id: [1u8; 32] }].into()),
|
||||
);
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target =
|
||||
Location::new(1, X3([Teyrchain(42), PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(2, X1([GlobalConsensus(Ethereum { chain_id: 1 })].into()));
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(2, X2([GlobalConsensus(Pezkuwi), Teyrchain(1000)].into()));
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(0, X2([PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
|
||||
// Other AH locations cannot alias anything.
|
||||
let origin = Location::new(1, X2([Teyrchain(1000), GeneralIndex(9)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X2([Teyrchain(1000), PalletInstance(9)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(
|
||||
1,
|
||||
X2([Teyrchain(1000), AccountId32 { network: None, id: [1u8; 32] }].into()),
|
||||
);
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
|
||||
// Other root locations cannot alias anything.
|
||||
let origin = Location::new(1, Here);
|
||||
let target = Location::new(2, X1([GlobalConsensus(Ethereum { chain_id: 1 })].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(2, X2([GlobalConsensus(Pezkuwi), Teyrchain(1000)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(0, X2([PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
|
||||
let origin = Location::new(0, Here);
|
||||
let target = Location::new(1, X1([Teyrchain(2000)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(1001)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(1002)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn authorized_cross_chain_aliases() {
|
||||
// origin and target are different accounts on different chains
|
||||
let origin: AccountId = [100; 32].into();
|
||||
let bad_origin: AccountId = [150; 32].into();
|
||||
let target: AccountId = [200; 32].into();
|
||||
let fees = ZAGROS_ED * 10;
|
||||
|
||||
let pal_admin = <PenpalB as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get());
|
||||
PenpalB::mint_foreign_asset(pal_admin.clone(), Location::parent(), origin.clone(), fees * 10);
|
||||
PenpalB::mint_foreign_asset(pal_admin, Location::parent(), bad_origin.clone(), fees * 10);
|
||||
CollectivesZagros::fund_accounts(vec![(target.clone(), fees * 10)]);
|
||||
|
||||
// let's authorize `origin` on Penpal to alias `target` on Collectives
|
||||
CollectivesZagros::execute_with(|| {
|
||||
let penpal_origin = Location::new(
|
||||
1,
|
||||
X2([
|
||||
Teyrchain(PenpalB::para_id().into()),
|
||||
AccountId32 {
|
||||
network: Some(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
id: origin.clone().into(),
|
||||
},
|
||||
]
|
||||
.into()),
|
||||
);
|
||||
// `target` adds `penpal_origin` as authorized alias
|
||||
assert_ok!(
|
||||
<CollectivesZagros as CollectivesZagrosPallet>::PezkuwiXcm::add_authorized_alias(
|
||||
<CollectivesZagros as Chain>::RuntimeOrigin::signed(target.clone()),
|
||||
Box::new(penpal_origin.into()),
|
||||
None
|
||||
)
|
||||
);
|
||||
});
|
||||
// Verify that unauthorized `bad_origin` cannot alias into `target`, from any chain.
|
||||
test_cross_chain_alias!(
|
||||
vec![
|
||||
// between AH and Collectives: denied
|
||||
(AssetHubZagros, CollectivesZagros, TELEPORT_FEES, DENIED),
|
||||
// between BH and Collectives: denied
|
||||
(BridgeHubZagros, CollectivesZagros, TELEPORT_FEES, DENIED),
|
||||
// between People and Collectives: denied
|
||||
(PeopleZagros, CollectivesZagros, TELEPORT_FEES, DENIED),
|
||||
// between Penpal and Collectives: denied
|
||||
(PenpalB, CollectivesZagros, RESERVE_TRANSFER_FEES, DENIED)
|
||||
],
|
||||
bad_origin,
|
||||
target,
|
||||
fees
|
||||
);
|
||||
// Verify that only authorized `penpal::origin` can alias into `target`, while `origin` on other
|
||||
// chains cannot.
|
||||
test_cross_chain_alias!(
|
||||
vec![
|
||||
// between AH and Collectives: denied
|
||||
(AssetHubZagros, CollectivesZagros, TELEPORT_FEES, DENIED),
|
||||
// between BH and Collectives: denied
|
||||
(BridgeHubZagros, CollectivesZagros, TELEPORT_FEES, DENIED),
|
||||
// between People and Collectives: denied
|
||||
(PeopleZagros, CollectivesZagros, TELEPORT_FEES, DENIED),
|
||||
// between Penpal and Collectives: allowed
|
||||
(PenpalB, CollectivesZagros, RESERVE_TRANSFER_FEES, ALLOWED)
|
||||
],
|
||||
origin,
|
||||
target,
|
||||
fees
|
||||
);
|
||||
// remove authorization for `origin` on Penpal to alias `target` on Collectives
|
||||
CollectivesZagros::execute_with(|| {
|
||||
// `target` removes all authorized aliases
|
||||
assert_ok!(
|
||||
<CollectivesZagros as CollectivesZagrosPallet>::PezkuwiXcm::remove_all_authorized_aliases(
|
||||
<CollectivesZagros as Chain>::RuntimeOrigin::signed(target.clone())
|
||||
)
|
||||
);
|
||||
});
|
||||
// Verify `penpal::origin` can no longer alias into `target` on Collectives.
|
||||
test_cross_chain_alias!(
|
||||
vec![(PenpalB, CollectivesZagros, RESERVE_TRANSFER_FEES, DENIED)],
|
||||
origin,
|
||||
target,
|
||||
fees
|
||||
);
|
||||
}
|
||||
+111
@@ -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.
|
||||
|
||||
use crate::imports::*;
|
||||
use collectives_zagros_runtime::{
|
||||
fellowship::FellowshipSalaryPaymaster, secretary::SecretarySalaryPaymaster,
|
||||
};
|
||||
use pezframe_support::{
|
||||
assert_ok,
|
||||
traits::{fungibles::Mutate, tokens::Pay},
|
||||
};
|
||||
use xcm_executor::traits::ConvertLocation;
|
||||
|
||||
const FELLOWSHIP_SALARY_PALLET_ID: u8 = 64;
|
||||
const SECRETARY_SALARY_PALLET_ID: u8 = 91;
|
||||
|
||||
#[test]
|
||||
fn pay_salary_technical_fellowship() {
|
||||
let asset_id: u32 = 1984;
|
||||
let fellowship_salary = (
|
||||
Parent,
|
||||
Teyrchain(CollectivesZagros::para_id().into()),
|
||||
PalletInstance(FELLOWSHIP_SALARY_PALLET_ID),
|
||||
);
|
||||
let pay_from =
|
||||
AssetHubLocationToAccountId::convert_location(&fellowship_salary.into()).unwrap();
|
||||
let pay_to = Zagros::account_id_of(ALICE);
|
||||
let pay_amount = 9_000_000_000;
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type AssetHubAssets = <AssetHubZagros as AssetHubZagrosPallet>::Assets;
|
||||
assert_ok!(<AssetHubAssets as Mutate<_>>::mint_into(asset_id, &pay_from, pay_amount * 2));
|
||||
});
|
||||
|
||||
CollectivesZagros::execute_with(|| {
|
||||
type RuntimeEvent = <CollectivesZagros as Chain>::RuntimeEvent;
|
||||
|
||||
assert_ok!(FellowshipSalaryPaymaster::pay(&pay_to, (), pay_amount));
|
||||
assert_expected_events!(
|
||||
CollectivesZagros,
|
||||
vec![
|
||||
RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::Assets(pezpallet_assets::Event::Transferred { .. }) => {},
|
||||
RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed { success: true ,.. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pay_salary_secretary() {
|
||||
const USDT_ID: u32 = 1984;
|
||||
let secretary_salary = (
|
||||
Parent,
|
||||
Teyrchain(CollectivesZagros::para_id().into()),
|
||||
PalletInstance(SECRETARY_SALARY_PALLET_ID),
|
||||
);
|
||||
let pay_from = AssetHubLocationToAccountId::convert_location(&secretary_salary.into()).unwrap();
|
||||
let pay_to = Zagros::account_id_of(ALICE);
|
||||
let pay_amount = 9_000_000_000;
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type AssetHubAssets = <AssetHubZagros as AssetHubZagrosPallet>::Assets;
|
||||
// USDT registered in genesis, now mint some into the payer's account
|
||||
assert_ok!(<AssetHubAssets as Mutate<_>>::mint_into(USDT_ID, &pay_from, pay_amount * 2));
|
||||
});
|
||||
|
||||
CollectivesZagros::execute_with(|| {
|
||||
type RuntimeEvent = <CollectivesZagros as Chain>::RuntimeEvent;
|
||||
|
||||
assert_ok!(SecretarySalaryPaymaster::pay(&pay_to, (), pay_amount));
|
||||
assert_expected_events!(
|
||||
CollectivesZagros,
|
||||
vec![
|
||||
RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::Assets(pezpallet_assets::Event::Transferred { .. }) => {},
|
||||
RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed { success: true ,.. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
// 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 codec::Encode;
|
||||
use collectives_fellowship::pezpallet_fellowship_origins::Origin::Fellows as FellowsOrigin;
|
||||
use pezframe_support::{assert_ok, pezsp_runtime::traits::Dispatchable};
|
||||
|
||||
#[test]
|
||||
fn fellows_whitelist_call() {
|
||||
CollectivesZagros::execute_with(|| {
|
||||
type RuntimeEvent = <CollectivesZagros as Chain>::RuntimeEvent;
|
||||
type RuntimeCall = <CollectivesZagros as Chain>::RuntimeCall;
|
||||
type RuntimeOrigin = <CollectivesZagros as Chain>::RuntimeOrigin;
|
||||
type Runtime = <CollectivesZagros as Chain>::Runtime;
|
||||
type ZagrosCall = <Zagros as Chain>::RuntimeCall;
|
||||
type ZagrosRuntime = <Zagros as Chain>::Runtime;
|
||||
|
||||
let call_hash = [1u8; 32].into();
|
||||
|
||||
let whitelist_call = RuntimeCall::PezkuwiXcm(pezpallet_xcm::Call::<Runtime>::send {
|
||||
dest: bx!(VersionedLocation::from(Location::parent())),
|
||||
message: bx!(VersionedXcm::from(Xcm(vec![
|
||||
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
|
||||
Transact {
|
||||
origin_kind: OriginKind::Xcm,
|
||||
call: ZagrosCall::Whitelist(
|
||||
pezpallet_whitelist::Call::<ZagrosRuntime>::whitelist_call { call_hash }
|
||||
)
|
||||
.encode()
|
||||
.into(),
|
||||
fallback_max_weight: None
|
||||
}
|
||||
]))),
|
||||
});
|
||||
|
||||
let fellows_origin: RuntimeOrigin = FellowsOrigin.into();
|
||||
|
||||
assert_ok!(whitelist_call.dispatch(fellows_origin));
|
||||
|
||||
assert_expected_events!(
|
||||
CollectivesZagros,
|
||||
vec![
|
||||
RuntimeEvent::PezkuwiXcm(pezpallet_xcm::Event::Sent { .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
Zagros::execute_with(|| {
|
||||
type RuntimeEvent = <Zagros as Chain>::RuntimeEvent;
|
||||
|
||||
assert_expected_events!(
|
||||
Zagros,
|
||||
vec![
|
||||
RuntimeEvent::Whitelist(pezpallet_whitelist::Event::CallWhitelisted { .. }) => {},
|
||||
RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed { success: true, .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
+242
@@ -0,0 +1,242 @@
|
||||
// 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::{
|
||||
assert_ok, dispatch::RawOrigin, instances::Instance1, pezsp_runtime::traits::Dispatchable,
|
||||
traits::fungible::Inspect,
|
||||
};
|
||||
use pezkuwi_runtime_common::impls::VersionedLocatableAsset;
|
||||
use xcm_executor::traits::ConvertLocation;
|
||||
use zagros_runtime_constants::currency::UNITS;
|
||||
use zagros_system_emulated_network::zagros_emulated_chain::zagros_runtime::Dmp;
|
||||
|
||||
// Fund Fellowship Treasury from Zagros Treasury and spend from Fellowship Treasury.
|
||||
#[test]
|
||||
fn fellowship_treasury_spend() {
|
||||
// initial treasury balance on Asset Hub in WNDs.
|
||||
let treasury_balance = 20_000_000 * UNITS;
|
||||
// target fellowship balance on Asset Hub in WNDs.
|
||||
let fellowship_treasury_balance = 1_000_000 * UNITS;
|
||||
// fellowship first spend balance in WNDs.
|
||||
let fellowship_spend_balance = 10_000 * UNITS;
|
||||
|
||||
let init_alice_balance = AssetHubZagros::execute_with(|| {
|
||||
<<AssetHubZagros as AssetHubZagrosPallet>::Balances as Inspect<_>>::balance(
|
||||
&AssetHubZagros::account_id_of(ALICE),
|
||||
)
|
||||
});
|
||||
|
||||
let check_account = AssetHubZagros::execute_with(|| {
|
||||
<AssetHubZagros as AssetHubZagrosPallet>::PezkuwiXcm::check_account()
|
||||
});
|
||||
// prefund Asset Hub checking account so we accept teleport from relay
|
||||
AssetHubZagros::fund_accounts(vec![(check_account, treasury_balance)]);
|
||||
|
||||
Zagros::execute_with(|| {
|
||||
type RuntimeEvent = <Zagros as Chain>::RuntimeEvent;
|
||||
type RuntimeCall = <Zagros as Chain>::RuntimeCall;
|
||||
type Runtime = <Zagros as Chain>::Runtime;
|
||||
type Balances = <Zagros as ZagrosPallet>::Balances;
|
||||
type Treasury = <Zagros as ZagrosPallet>::Treasury;
|
||||
|
||||
// Fund Treasury account on Asset Hub with WNDs.
|
||||
|
||||
let root = <Zagros as Chain>::RuntimeOrigin::root();
|
||||
let treasury_account = Treasury::account_id();
|
||||
|
||||
// Mist 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(37)).into();
|
||||
|
||||
let teleport_call = RuntimeCall::Utility(pezpallet_utility::Call::<Runtime>::dispatch_as {
|
||||
as_origin: bx!(ZagrosOriginCaller::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!(
|
||||
Zagros,
|
||||
vec![
|
||||
RuntimeEvent::XcmPallet(pezpallet_xcm::Event::Sent { .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
Zagros::execute_with(|| {
|
||||
type RuntimeEvent = <Zagros as Chain>::RuntimeEvent;
|
||||
type RuntimeCall = <Zagros as Chain>::RuntimeCall;
|
||||
type RuntimeOrigin = <Zagros as Chain>::RuntimeOrigin;
|
||||
type Runtime = <Zagros as Chain>::Runtime;
|
||||
type Treasury = <Zagros as ZagrosPallet>::Treasury;
|
||||
|
||||
// Fund Fellowship Treasury from Zagros Treasury.
|
||||
|
||||
let treasury_origin: RuntimeOrigin =
|
||||
zagros_governance::pezpallet_custom_origins::Origin::Treasurer.into();
|
||||
let fellowship_treasury_location: Location =
|
||||
Location::new(1, [Teyrchain(1001), PalletInstance(65)]);
|
||||
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: fellowship_treasury_balance,
|
||||
beneficiary: bx!(VersionedLocation::from(fellowship_treasury_location)),
|
||||
valid_from: None,
|
||||
});
|
||||
|
||||
assert_ok!(treasury_spend_call.dispatch(treasury_origin));
|
||||
|
||||
// Claim the spend.
|
||||
|
||||
let alice_signed = RuntimeOrigin::signed(Zagros::account_id_of(ALICE));
|
||||
assert_ok!(Treasury::payout(alice_signed.clone(), 0));
|
||||
|
||||
assert_expected_events!(
|
||||
Zagros,
|
||||
vec![
|
||||
RuntimeEvent::Treasury(pezpallet_treasury::Event::AssetSpendApproved { .. }) => {},
|
||||
RuntimeEvent::Treasury(pezpallet_treasury::Event::Paid { .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
type Balances = <AssetHubZagros as AssetHubZagrosPallet>::Balances;
|
||||
|
||||
// Ensure that the funds deposited to the Fellowship Treasury account.
|
||||
|
||||
let fellowship_treasury_location: Location =
|
||||
Location::new(1, [Teyrchain(1001), PalletInstance(65)]);
|
||||
let fellowship_treasury_account =
|
||||
AssetHubLocationToAccountId::convert_location(&fellowship_treasury_location).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
<Balances as Inspect<_>>::balance(&fellowship_treasury_account),
|
||||
fellowship_treasury_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!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::Balances(pezpallet_balances::Event::Transfer { .. }) => {},
|
||||
RuntimeEvent::TeyrchainSystem(cumulus_pallet_teyrchain_system::Event::UpwardMessageSent { .. }) => {},
|
||||
RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed { success: true ,.. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
CollectivesZagros::execute_with(|| {
|
||||
type RuntimeEvent = <CollectivesZagros as Chain>::RuntimeEvent;
|
||||
type RuntimeCall = <CollectivesZagros as Chain>::RuntimeCall;
|
||||
type RuntimeOrigin = <CollectivesZagros as Chain>::RuntimeOrigin;
|
||||
type Runtime = <CollectivesZagros as Chain>::Runtime;
|
||||
type FellowshipTreasury =
|
||||
<CollectivesZagros as CollectivesZagrosPallet>::FellowshipTreasury;
|
||||
|
||||
// Fund Alice account from Fellowship Treasury.
|
||||
|
||||
let fellows_origin: RuntimeOrigin =
|
||||
collectives_fellowship::pezpallet_fellowship_origins::Origin::Fellows.into();
|
||||
let asset_hub_location: Location = (Parent, Teyrchain(1000)).into();
|
||||
let native_asset = Location::parent();
|
||||
|
||||
let alice_location: Location = [Junction::AccountId32 {
|
||||
network: None,
|
||||
id: CollectivesZagros::account_id_of(ALICE).into(),
|
||||
}]
|
||||
.into();
|
||||
|
||||
let fellowship_treasury_spend_call =
|
||||
RuntimeCall::FellowshipTreasury(pezpallet_treasury::Call::<Runtime, Instance1>::spend {
|
||||
asset_kind: bx!(VersionedLocatableAsset::from((
|
||||
asset_hub_location,
|
||||
native_asset.into()
|
||||
))),
|
||||
amount: fellowship_spend_balance,
|
||||
beneficiary: bx!(VersionedLocation::from(alice_location)),
|
||||
valid_from: None,
|
||||
});
|
||||
|
||||
assert_ok!(fellowship_treasury_spend_call.dispatch(fellows_origin));
|
||||
|
||||
// Claim the spend.
|
||||
|
||||
let alice_signed = RuntimeOrigin::signed(CollectivesZagros::account_id_of(ALICE));
|
||||
assert_ok!(FellowshipTreasury::payout(alice_signed.clone(), 0));
|
||||
|
||||
assert_expected_events!(
|
||||
CollectivesZagros,
|
||||
vec![
|
||||
RuntimeEvent::FellowshipTreasury(pezpallet_treasury::Event::AssetSpendApproved { .. }) => {},
|
||||
RuntimeEvent::FellowshipTreasury(pezpallet_treasury::Event::Paid { .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
type Balances = <AssetHubZagros as AssetHubZagrosPallet>::Balances;
|
||||
|
||||
// Ensure that the funds deposited to Alice account.
|
||||
|
||||
let alice_account = AssetHubZagros::account_id_of(ALICE);
|
||||
assert_eq!(
|
||||
<Balances as Inspect<_>>::balance(&alice_account),
|
||||
fellowship_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!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::Balances(pezpallet_balances::Event::Transfer { .. }) => {},
|
||||
RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},
|
||||
RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed { success: true ,.. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// 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 aliases;
|
||||
mod collectives_salary;
|
||||
mod fellowship;
|
||||
mod fellowship_treasury;
|
||||
mod teleport;
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
// 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::{
|
||||
test_relay_is_trusted_teleporter, test_teyrchain_is_trusted_teleporter_for_relay,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn teleport_via_limited_teleport_assets_from_and_to_relay() {
|
||||
let amount = ZAGROS_ED * 10;
|
||||
|
||||
test_relay_is_trusted_teleporter!(
|
||||
Zagros, // Origin
|
||||
vec![CollectivesZagros], // Destinations
|
||||
amount,
|
||||
limited_teleport_assets
|
||||
);
|
||||
|
||||
test_teyrchain_is_trusted_teleporter_for_relay!(
|
||||
CollectivesZagros, // Origin
|
||||
Zagros, // Destination
|
||||
amount,
|
||||
limited_teleport_assets
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleport_via_transfer_assets_from_and_to_relay() {
|
||||
let amount = ZAGROS_ED * 10;
|
||||
|
||||
test_relay_is_trusted_teleporter!(
|
||||
Zagros, // Origin
|
||||
vec![CollectivesZagros], // Destinations
|
||||
amount,
|
||||
transfer_assets
|
||||
);
|
||||
|
||||
test_teyrchain_is_trusted_teleporter_for_relay!(
|
||||
CollectivesZagros, // Origin
|
||||
Zagros, // Destination
|
||||
amount,
|
||||
transfer_assets
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleport_via_limited_teleport_assets_from_collectives_to_asset_hub() {
|
||||
let amount = ASSET_HUB_ZAGROS_ED * 100;
|
||||
let native_asset: Assets = (Parent, amount).into();
|
||||
|
||||
let fee_asset_id: AssetId = Parent.into();
|
||||
test_teyrchain_is_trusted_teleporter!(
|
||||
CollectivesZagros, // Origin
|
||||
vec![AssetHubZagros], // Destinations
|
||||
(native_asset, amount),
|
||||
fee_asset_id,
|
||||
limited_teleport_assets
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleport_via_transfer_assets_from_collectives_to_asset_hub() {
|
||||
let amount = ASSET_HUB_ZAGROS_ED * 100;
|
||||
let native_asset: Assets = (Parent, amount).into();
|
||||
|
||||
let fee_asset_id: AssetId = Parent.into();
|
||||
test_teyrchain_is_trusted_teleporter!(
|
||||
CollectivesZagros, // Origin
|
||||
vec![AssetHubZagros], // Destinations
|
||||
(native_asset, amount),
|
||||
fee_asset_id,
|
||||
transfer_assets
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleport_via_limited_teleport_assets_from_asset_hub_to_collectives() {
|
||||
let amount = COLLECTIVES_ZAGROS_ED * 100;
|
||||
let native_asset: Assets = (Parent, amount).into();
|
||||
|
||||
let fee_asset_id: AssetId = Parent.into();
|
||||
test_teyrchain_is_trusted_teleporter!(
|
||||
AssetHubZagros, // Origin
|
||||
vec![CollectivesZagros], // Destinations
|
||||
(native_asset, amount),
|
||||
fee_asset_id,
|
||||
limited_teleport_assets
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleport_via_transfer_assets_from_asset_hub_to_collectives() {
|
||||
let amount = COLLECTIVES_ZAGROS_ED * 100;
|
||||
let native_asset: Assets = (Parent, amount).into();
|
||||
|
||||
let fee_asset_id: AssetId = Parent.into();
|
||||
test_teyrchain_is_trusted_teleporter!(
|
||||
AssetHubZagros, // Origin
|
||||
vec![CollectivesZagros], // Destinations
|
||||
(native_asset, amount),
|
||||
fee_asset_id,
|
||||
transfer_assets
|
||||
);
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
[package]
|
||||
name = "coretime-pezkuwichain-integration-tests"
|
||||
version = "0.0.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license = "Apache-2.0"
|
||||
description = "Coretime Pezkuwichain runtime integration tests with xcm-emulator"
|
||||
publish = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
# Bizinikiwi
|
||||
pezframe-support = { workspace = true }
|
||||
pezpallet-broker = { workspace = true, default-features = true }
|
||||
pezpallet-message-queue = { workspace = true }
|
||||
pezsp-runtime = { workspace = true }
|
||||
|
||||
# Pezkuwi
|
||||
pezkuwi-runtime-teyrchains = { workspace = true, default-features = true }
|
||||
pezkuwichain-runtime-constants = { workspace = true, default-features = true }
|
||||
xcm = { workspace = true }
|
||||
|
||||
# Pezcumulus
|
||||
pezcumulus-pezpallet-teyrchain-system = { workspace = true, default-features = true }
|
||||
emulated-integration-tests-common = { workspace = true }
|
||||
pezkuwichain-system-emulated-network = { workspace = true }
|
||||
|
||||
[features]
|
||||
runtime-benchmarks = [
|
||||
"pezcumulus-pezpallet-teyrchain-system/runtime-benchmarks",
|
||||
"emulated-integration-tests-common/runtime-benchmarks",
|
||||
"pezframe-support/runtime-benchmarks",
|
||||
"pezpallet-broker/runtime-benchmarks",
|
||||
"pezpallet-message-queue/runtime-benchmarks",
|
||||
"pezkuwi-runtime-teyrchains/runtime-benchmarks",
|
||||
"pezkuwichain-runtime-constants/runtime-benchmarks",
|
||||
"pezkuwichain-system-emulated-network/runtime-benchmarks",
|
||||
"pezsp-runtime/runtime-benchmarks",
|
||||
"xcm/runtime-benchmarks",
|
||||
]
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
// 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 {
|
||||
|
||||
// Bizinikiwi
|
||||
pub(crate) use pezframe_support::assert_ok;
|
||||
|
||||
// Pezkuwi
|
||||
pub(crate) use xcm::{latest::PEZKUWICHAIN_GENESIS_HASH, prelude::*};
|
||||
|
||||
// Pezcumulus
|
||||
pub(crate) use emulated_integration_tests_common::xcm_emulator::{
|
||||
assert_expected_events, Chain, TestExt, Teyrchain,
|
||||
};
|
||||
pub(crate) use pezkuwichain_system_emulated_network::{
|
||||
asset_hub_pezkuwichain_emulated_chain::genesis::ED as ASSET_HUB_PEZKUWICHAIN_ED,
|
||||
coretime_pezkuwichain_emulated_chain::{
|
||||
coretime_pezkuwichain_runtime::ExistentialDeposit as CoretimePezkuwichainExistentialDeposit,
|
||||
genesis::ED as CORETIME_PEZKUWICHAIN_ED,
|
||||
CoretimePezkuwichainParaPallet as CoretimePezkuwichainPallet,
|
||||
},
|
||||
pezkuwichain_emulated_chain::{
|
||||
genesis::ED as PEZKUWICHAIN_ED, PezkuwichainRelayPallet as PezkuwichainPallet,
|
||||
},
|
||||
AssetHubPezkuwichainPara as AssetHubPezkuwichain,
|
||||
AssetHubPezkuwichainParaReceiver as AssetHubPezkuwichainReceiver,
|
||||
AssetHubPezkuwichainParaSender as AssetHubPezkuwichainSender,
|
||||
CoretimePezkuwichainPara as CoretimePezkuwichain,
|
||||
CoretimePezkuwichainParaReceiver as CoretimePezkuwichainReceiver,
|
||||
CoretimePezkuwichainParaSender as CoretimePezkuwichainSender,
|
||||
PezkuwichainRelay as Pezkuwichain, PezkuwichainRelayReceiver as PezkuwichainReceiver,
|
||||
PezkuwichainRelaySender as PezkuwichainSender,
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
+34
@@ -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 = CoretimePezkuwichainExistentialDeposit::get();
|
||||
let assets: Assets = (Parent, amount).into();
|
||||
|
||||
test_chain_can_claim_assets!(
|
||||
CoretimePezkuwichain,
|
||||
RuntimeCall,
|
||||
NetworkId::ByGenesis(PEZKUWICHAIN_GENESIS_HASH),
|
||||
assets,
|
||||
amount
|
||||
);
|
||||
}
|
||||
+241
@@ -0,0 +1,241 @@
|
||||
// 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::traits::OnInitialize;
|
||||
use pezpallet_broker::{ConfigRecord, Configuration, CoreAssignment, CoreMask, ScheduleItem};
|
||||
use pezkuwichain_runtime_constants::system_teyrchain::coretime::TIMESLICE_PERIOD;
|
||||
use pezkuwichain_system_emulated_network::pezkuwichain_emulated_chain::pezkuwichain_runtime::Dmp;
|
||||
use pezsp_runtime::Perbill;
|
||||
|
||||
#[test]
|
||||
fn transact_hardcoded_weights_are_sane() {
|
||||
// There are three transacts with hardcoded weights sent from the Coretime Chain to the Relay
|
||||
// Chain across the CoretimeInterface which are triggered at various points in the sales cycle.
|
||||
// - Request core count - triggered directly by `start_sales` or `request_core_count`
|
||||
// extrinsics.
|
||||
// - Request revenue info - triggered when each timeslice is committed.
|
||||
// - Assign core - triggered when an entry is encountered in the workplan for the next
|
||||
// timeslice.
|
||||
|
||||
// RuntimeEvent aliases to avoid warning from usage of qualified paths in assertions due to
|
||||
// <https://github.com/rust-lang/rust/issues/86935>
|
||||
type CoretimeEvent = <CoretimePezkuwichain as Chain>::RuntimeEvent;
|
||||
type RelayEvent = <Pezkuwichain as Chain>::RuntimeEvent;
|
||||
|
||||
Pezkuwichain::execute_with(|| {
|
||||
Dmp::make_teyrchain_reachable(CoretimePezkuwichain::para_id());
|
||||
});
|
||||
|
||||
// Reserve a workload, configure broker and start sales.
|
||||
CoretimePezkuwichain::execute_with(|| {
|
||||
// Hooks don't run in emulated tests - workaround as we need `on_initialize` to tick things
|
||||
// along and have no concept of time passing otherwise.
|
||||
<CoretimePezkuwichain as CoretimePezkuwichainPallet>::Broker::on_initialize(
|
||||
<CoretimePezkuwichain as Chain>::System::block_number(),
|
||||
);
|
||||
|
||||
let coretime_root_origin = <CoretimePezkuwichain as Chain>::RuntimeOrigin::root();
|
||||
|
||||
// Create and populate schedule with the worst case assignment on this core.
|
||||
let mut schedule = Vec::new();
|
||||
for i in 0..80 {
|
||||
schedule.push(ScheduleItem {
|
||||
mask: CoreMask::void().set(i),
|
||||
assignment: CoreAssignment::Task(2000 + i),
|
||||
})
|
||||
}
|
||||
|
||||
assert_ok!(<CoretimePezkuwichain as CoretimePezkuwichainPallet>::Broker::reserve(
|
||||
coretime_root_origin.clone(),
|
||||
schedule.try_into().expect("Vector is within bounds."),
|
||||
));
|
||||
|
||||
// Configure broker and start sales.
|
||||
let config = ConfigRecord {
|
||||
advance_notice: 1,
|
||||
interlude_length: 1,
|
||||
leadin_length: 2,
|
||||
region_length: 1,
|
||||
ideal_bulk_proportion: Perbill::from_percent(40),
|
||||
limit_cores_offered: None,
|
||||
renewal_bump: Perbill::from_percent(2),
|
||||
contribution_timeout: 1,
|
||||
};
|
||||
assert_ok!(<CoretimePezkuwichain as CoretimePezkuwichainPallet>::Broker::configure(
|
||||
coretime_root_origin.clone(),
|
||||
config
|
||||
));
|
||||
assert_ok!(<CoretimePezkuwichain as CoretimePezkuwichainPallet>::Broker::start_sales(
|
||||
coretime_root_origin,
|
||||
100,
|
||||
0
|
||||
));
|
||||
assert_eq!(
|
||||
pezpallet_broker::Status::<<CoretimePezkuwichain as Chain>::Runtime>::get()
|
||||
.unwrap()
|
||||
.core_count,
|
||||
1
|
||||
);
|
||||
|
||||
assert_expected_events!(
|
||||
CoretimePezkuwichain,
|
||||
vec![
|
||||
CoretimeEvent::Broker(
|
||||
pezpallet_broker::Event::ReservationMade { .. }
|
||||
) => {},
|
||||
CoretimeEvent::Broker(
|
||||
pezpallet_broker::Event::CoreCountRequested { core_count: 1 }
|
||||
) => {},
|
||||
CoretimeEvent::TeyrchainSystem(
|
||||
cumulus_pallet_teyrchain_system::Event::UpwardMessageSent { .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
// Check that the request_core_count message was processed successfully. This will fail if the
|
||||
// weights are misconfigured.
|
||||
Pezkuwichain::execute_with(|| {
|
||||
Pezkuwichain::assert_ump_queue_processed(true, Some(CoretimePezkuwichain::para_id()), None);
|
||||
|
||||
assert_expected_events!(
|
||||
Pezkuwichain,
|
||||
vec![
|
||||
RelayEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
// Keep track of the relay chain block number so we can fast forward while still checking the
|
||||
// right block.
|
||||
let mut block_number_cursor =
|
||||
Pezkuwichain::ext_wrapper(<Pezkuwichain as Chain>::System::block_number);
|
||||
|
||||
let config = CoretimePezkuwichain::ext_wrapper(|| {
|
||||
Configuration::<<CoretimePezkuwichain as Chain>::Runtime>::get()
|
||||
.expect("Pallet was configured earlier.")
|
||||
});
|
||||
|
||||
// Now run up to the block before the sale is rotated.
|
||||
while block_number_cursor < TIMESLICE_PERIOD - config.advance_notice - 1 {
|
||||
CoretimePezkuwichain::execute_with(|| {
|
||||
// Hooks don't run in emulated tests - workaround.
|
||||
<CoretimePezkuwichain as CoretimePezkuwichainPallet>::Broker::on_initialize(
|
||||
<CoretimePezkuwichain as Chain>::System::block_number(),
|
||||
);
|
||||
});
|
||||
|
||||
Pezkuwichain::ext_wrapper(|| {
|
||||
block_number_cursor = <Pezkuwichain as Chain>::System::block_number();
|
||||
});
|
||||
}
|
||||
|
||||
// In this block we trigger assign core.
|
||||
CoretimePezkuwichain::execute_with(|| {
|
||||
// Hooks don't run in emulated tests - workaround.
|
||||
<CoretimePezkuwichain as CoretimePezkuwichainPallet>::Broker::on_initialize(
|
||||
<CoretimePezkuwichain as Chain>::System::block_number(),
|
||||
);
|
||||
|
||||
assert_expected_events!(
|
||||
CoretimePezkuwichain,
|
||||
vec![
|
||||
CoretimeEvent::Broker(
|
||||
pezpallet_broker::Event::SaleInitialized { .. }
|
||||
) => {},
|
||||
CoretimeEvent::Broker(
|
||||
pezpallet_broker::Event::CoreAssigned { .. }
|
||||
) => {},
|
||||
CoretimeEvent::TeyrchainSystem(
|
||||
cumulus_pallet_teyrchain_system::Event::UpwardMessageSent { .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
// Check that the assign_core message was processed successfully.
|
||||
// This will fail if the weights are misconfigured.
|
||||
Pezkuwichain::execute_with(|| {
|
||||
Pezkuwichain::assert_ump_queue_processed(true, Some(CoretimePezkuwichain::para_id()), None);
|
||||
|
||||
assert_expected_events!(
|
||||
Pezkuwichain,
|
||||
vec![
|
||||
RelayEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
RelayEvent::Coretime(
|
||||
pezkuwi_runtime_teyrchains::coretime::Event::CoreAssigned { .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
// In this block we trigger request revenue.
|
||||
CoretimePezkuwichain::execute_with(|| {
|
||||
// Hooks don't run in emulated tests - workaround.
|
||||
<CoretimePezkuwichain as CoretimePezkuwichainPallet>::Broker::on_initialize(
|
||||
<CoretimePezkuwichain as Chain>::System::block_number(),
|
||||
);
|
||||
|
||||
assert_expected_events!(
|
||||
CoretimePezkuwichain,
|
||||
vec![
|
||||
CoretimeEvent::TeyrchainSystem(
|
||||
cumulus_pallet_teyrchain_system::Event::UpwardMessageSent { .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
// Check that the request_revenue_info_at message was processed successfully.
|
||||
// This will fail if the weights are misconfigured.
|
||||
Pezkuwichain::execute_with(|| {
|
||||
Pezkuwichain::assert_ump_queue_processed(true, Some(CoretimePezkuwichain::para_id()), None);
|
||||
|
||||
assert_expected_events!(
|
||||
Pezkuwichain,
|
||||
vec![
|
||||
RelayEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
// Here we receive and process the notify_revenue XCM with zero revenue.
|
||||
CoretimePezkuwichain::execute_with(|| {
|
||||
// Hooks don't run in emulated tests - workaround.
|
||||
<CoretimePezkuwichain as CoretimePezkuwichainPallet>::Broker::on_initialize(
|
||||
<CoretimePezkuwichain as Chain>::System::block_number(),
|
||||
);
|
||||
|
||||
assert_expected_events!(
|
||||
CoretimePezkuwichain,
|
||||
vec![
|
||||
CoretimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
// Zero revenue in first timeslice so history is immediately dropped.
|
||||
CoretimeEvent::Broker(
|
||||
pezpallet_broker::Event::HistoryDropped { when: 0, revenue: 0 }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// 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 coretime_interface;
|
||||
mod teleport;
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
// 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::{
|
||||
test_relay_is_trusted_teleporter, test_teyrchain_is_trusted_teleporter,
|
||||
test_teyrchain_is_trusted_teleporter_for_relay,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn teleport_via_limited_teleport_assets_from_and_to_relay() {
|
||||
let amount = PEZKUWICHAIN_ED * 10;
|
||||
|
||||
test_relay_is_trusted_teleporter!(
|
||||
Pezkuwichain, // Origin
|
||||
vec![CoretimePezkuwichain], // Destinations
|
||||
amount,
|
||||
limited_teleport_assets
|
||||
);
|
||||
|
||||
test_teyrchain_is_trusted_teleporter_for_relay!(
|
||||
CoretimePezkuwichain, // Origin
|
||||
Pezkuwichain, // Destination
|
||||
amount,
|
||||
limited_teleport_assets
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleport_via_transfer_assets_from_and_to_relay() {
|
||||
let amount = PEZKUWICHAIN_ED * 10;
|
||||
|
||||
test_relay_is_trusted_teleporter!(
|
||||
Pezkuwichain, // Origin
|
||||
vec![CoretimePezkuwichain], // Destinations
|
||||
amount,
|
||||
transfer_assets
|
||||
);
|
||||
|
||||
test_teyrchain_is_trusted_teleporter_for_relay!(
|
||||
CoretimePezkuwichain, // Origin
|
||||
Pezkuwichain, // Destination
|
||||
amount,
|
||||
transfer_assets
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleport_via_limited_teleport_assets_from_coretime_to_asset_hub() {
|
||||
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!(
|
||||
CoretimePezkuwichain, // Origin
|
||||
vec![AssetHubPezkuwichain], // Destinations
|
||||
(native_asset, amount),
|
||||
fee_asset_id,
|
||||
limited_teleport_assets
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleport_via_transfer_assets_from_coretime_to_asset_hub() {
|
||||
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!(
|
||||
CoretimePezkuwichain, // Origin
|
||||
vec![AssetHubPezkuwichain], // Destinations
|
||||
(native_asset, amount),
|
||||
fee_asset_id,
|
||||
transfer_assets
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleport_via_limited_teleport_assets_from_asset_hub_to_coretime() {
|
||||
let amount = CORETIME_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![CoretimePezkuwichain], // Destinations
|
||||
(native_asset, amount),
|
||||
fee_asset_id,
|
||||
limited_teleport_assets
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleport_via_transfer_assets_from_asset_hub_to_coretime() {
|
||||
let amount = CORETIME_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![CoretimePezkuwichain], // Destinations
|
||||
(native_asset, amount),
|
||||
fee_asset_id,
|
||||
transfer_assets
|
||||
);
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
[package]
|
||||
name = "coretime-zagros-integration-tests"
|
||||
version = "0.0.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license = "Apache-2.0"
|
||||
description = "Coretime Zagros runtime integration tests with xcm-emulator"
|
||||
publish = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
# Bizinikiwi
|
||||
pezframe-support = { workspace = true }
|
||||
pezpallet-broker = { workspace = true, default-features = true }
|
||||
pezpallet-message-queue = { workspace = true }
|
||||
pezsp-runtime = { workspace = true }
|
||||
|
||||
# Pezkuwi
|
||||
pezkuwi-runtime-teyrchains = { workspace = true, default-features = true }
|
||||
xcm = { workspace = true }
|
||||
xcm-executor = { workspace = true }
|
||||
zagros-runtime-constants = { workspace = true, default-features = true }
|
||||
|
||||
# Pezcumulus
|
||||
pezcumulus-pezpallet-teyrchain-system = { workspace = true, default-features = true }
|
||||
emulated-integration-tests-common = { workspace = true }
|
||||
zagros-system-emulated-network = { workspace = true }
|
||||
|
||||
[features]
|
||||
runtime-benchmarks = [
|
||||
"pezcumulus-pezpallet-teyrchain-system/runtime-benchmarks",
|
||||
"emulated-integration-tests-common/runtime-benchmarks",
|
||||
"pezframe-support/runtime-benchmarks",
|
||||
"pezpallet-broker/runtime-benchmarks",
|
||||
"pezpallet-message-queue/runtime-benchmarks",
|
||||
"pezkuwi-runtime-teyrchains/runtime-benchmarks",
|
||||
"pezsp-runtime/runtime-benchmarks",
|
||||
"xcm-executor/runtime-benchmarks",
|
||||
"xcm/runtime-benchmarks",
|
||||
"zagros-runtime-constants/runtime-benchmarks",
|
||||
"zagros-system-emulated-network/runtime-benchmarks",
|
||||
]
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
// 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 {
|
||||
|
||||
// Bizinikiwi
|
||||
pub(crate) use pezframe_support::assert_ok;
|
||||
|
||||
// Pezkuwi
|
||||
pub(crate) use xcm::{latest::ZAGROS_GENESIS_HASH, prelude::*};
|
||||
|
||||
// Pezcumulus
|
||||
pub(crate) use emulated_integration_tests_common::xcm_emulator::{
|
||||
assert_expected_events, Chain, TestExt, Teyrchain,
|
||||
};
|
||||
pub(crate) use zagros_system_emulated_network::{
|
||||
asset_hub_zagros_emulated_chain::{
|
||||
genesis::ED as ASSET_HUB_ZAGROS_ED, AssetHubZagrosParaPallet as AssetHubZagrosPallet,
|
||||
},
|
||||
bridge_hub_zagros_emulated_chain::BridgeHubZagrosParaPallet as BridgeHubZagrosPallet,
|
||||
collectives_zagros_emulated_chain::CollectivesZagrosParaPallet as CollectivesZagrosPallet,
|
||||
coretime_zagros_emulated_chain::{
|
||||
self, coretime_zagros_runtime::ExistentialDeposit as CoretimeZagrosExistentialDeposit,
|
||||
genesis::ED as CORETIME_ZAGROS_ED, CoretimeZagrosParaPallet as CoretimeZagrosPallet,
|
||||
},
|
||||
penpal_emulated_chain::{PenpalAssetOwner, PenpalBParaPallet as PenpalBPallet},
|
||||
people_zagros_emulated_chain::PeopleZagrosParaPallet as PeopleZagrosPallet,
|
||||
zagros_emulated_chain::{genesis::ED as ZAGROS_ED, ZagrosRelayPallet as ZagrosPallet},
|
||||
AssetHubZagrosPara as AssetHubZagros, AssetHubZagrosParaReceiver as AssetHubZagrosReceiver,
|
||||
AssetHubZagrosParaSender as AssetHubZagrosSender, BridgeHubZagrosPara as BridgeHubZagros,
|
||||
CollectivesZagrosPara as CollectivesZagros, CoretimeZagrosPara as CoretimeZagros,
|
||||
CoretimeZagrosParaReceiver as CoretimeZagrosReceiver,
|
||||
CoretimeZagrosParaSender as CoretimeZagrosSender, PenpalBPara as PenpalB,
|
||||
PeopleZagrosPara as PeopleZagros, ZagrosRelay as Zagros,
|
||||
ZagrosRelayReceiver as ZagrosReceiver, ZagrosRelaySender as ZagrosSender,
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
+272
@@ -0,0 +1,272 @@
|
||||
// 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 XCM aliasing.
|
||||
|
||||
use crate::imports::*;
|
||||
use coretime_zagros_emulated_chain::coretime_zagros_runtime::xcm_config::XcmConfig;
|
||||
use emulated_integration_tests_common::{macros::AccountId, test_cross_chain_alias};
|
||||
use pezframe_support::traits::ContainsPair;
|
||||
use xcm::latest::Junctions::*;
|
||||
|
||||
const ALLOWED: bool = true;
|
||||
const DENIED: bool = false;
|
||||
|
||||
const TELEPORT_FEES: bool = true;
|
||||
const RESERVE_TRANSFER_FEES: bool = false;
|
||||
|
||||
#[test]
|
||||
fn account_on_sibling_syschain_aliases_into_same_local_account() {
|
||||
// origin and target are the same account on different chains
|
||||
let origin: AccountId = [1; 32].into();
|
||||
let target = origin.clone();
|
||||
let fees = ZAGROS_ED * 10;
|
||||
|
||||
PenpalB::mint_foreign_asset(
|
||||
<PenpalB as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get()),
|
||||
Location::parent(),
|
||||
origin.clone(),
|
||||
fees * 10,
|
||||
);
|
||||
|
||||
// Aliasing same account on different chains
|
||||
test_cross_chain_alias!(
|
||||
vec![
|
||||
// between AH and Coretime: allowed
|
||||
(AssetHubZagros, CoretimeZagros, TELEPORT_FEES, ALLOWED),
|
||||
// between BH and Coretime: allowed
|
||||
(BridgeHubZagros, CoretimeZagros, TELEPORT_FEES, ALLOWED),
|
||||
// between Collectives and Coretime: allowed
|
||||
(CollectivesZagros, CoretimeZagros, TELEPORT_FEES, ALLOWED),
|
||||
// between People and Coretime: allowed
|
||||
(PeopleZagros, CoretimeZagros, TELEPORT_FEES, ALLOWED),
|
||||
// between Penpal and Coretime: denied
|
||||
(PenpalB, CoretimeZagros, RESERVE_TRANSFER_FEES, DENIED)
|
||||
],
|
||||
origin,
|
||||
target,
|
||||
fees
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn account_on_sibling_syschain_cannot_alias_into_different_local_account() {
|
||||
// origin and target are different accounts on different chains
|
||||
let origin: AccountId = [1; 32].into();
|
||||
let target: AccountId = [2; 32].into();
|
||||
let fees = ZAGROS_ED * 10;
|
||||
|
||||
PenpalB::mint_foreign_asset(
|
||||
<PenpalB as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get()),
|
||||
Location::parent(),
|
||||
origin.clone(),
|
||||
fees * 10,
|
||||
);
|
||||
|
||||
// Aliasing different account on different chains
|
||||
test_cross_chain_alias!(
|
||||
vec![
|
||||
// between AH and Coretime: denied
|
||||
(AssetHubZagros, CoretimeZagros, TELEPORT_FEES, DENIED),
|
||||
// between BH and Coretime: denied
|
||||
(BridgeHubZagros, CoretimeZagros, TELEPORT_FEES, DENIED),
|
||||
// between Collectives and Coretime: denied
|
||||
(CollectivesZagros, CoretimeZagros, TELEPORT_FEES, DENIED),
|
||||
// between People and Coretime: denied
|
||||
(PeopleZagros, CoretimeZagros, TELEPORT_FEES, DENIED),
|
||||
// between Penpal and Coretime: denied
|
||||
(PenpalB, CoretimeZagros, RESERVE_TRANSFER_FEES, DENIED)
|
||||
],
|
||||
origin,
|
||||
target,
|
||||
fees
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aliasing_child_locations() {
|
||||
CoretimeZagros::execute_with(|| {
|
||||
// Allows aliasing descendant of origin.
|
||||
let origin = Location::new(1, X1([PalletInstance(8)].into()));
|
||||
let target = Location::new(1, X2([PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(8)].into()));
|
||||
let target = Location::new(
|
||||
1,
|
||||
X2([Teyrchain(8), AccountId32 { network: None, id: [1u8; 32] }].into()),
|
||||
);
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(8)].into()));
|
||||
let target =
|
||||
Location::new(1, X3([Teyrchain(8), PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
|
||||
// Does not allow if not descendant.
|
||||
let origin = Location::new(1, X1([PalletInstance(8)].into()));
|
||||
let target = Location::new(0, X2([PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(8)].into()));
|
||||
let target = Location::new(
|
||||
0,
|
||||
X2([Teyrchain(8), AccountId32 { network: None, id: [1u8; 32] }].into()),
|
||||
);
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(8)].into()));
|
||||
let target = Location::new(0, X1([AccountId32 { network: None, id: [1u8; 32] }].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([AccountId32 { network: None, id: [1u8; 32] }].into()));
|
||||
let target = Location::new(0, X1([AccountId32 { network: None, id: [1u8; 32] }].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn asset_hub_root_aliases_anything() {
|
||||
CoretimeZagros::execute_with(|| {
|
||||
// Allows AH root to alias anything.
|
||||
let origin = Location::new(1, X1([Teyrchain(1000)].into()));
|
||||
|
||||
let target = Location::new(1, X1([Teyrchain(2000)].into()));
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(1, X1([AccountId32 { network: None, id: [1u8; 32] }].into()));
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(
|
||||
1,
|
||||
X2([Teyrchain(8), AccountId32 { network: None, id: [1u8; 32] }].into()),
|
||||
);
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target =
|
||||
Location::new(1, X3([Teyrchain(42), PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(2, X1([GlobalConsensus(Ethereum { chain_id: 1 })].into()));
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(2, X2([GlobalConsensus(Pezkuwi), Teyrchain(1000)].into()));
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(0, X2([PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
|
||||
// Other AH locations cannot alias anything.
|
||||
let origin = Location::new(1, X2([Teyrchain(1000), GeneralIndex(9)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X2([Teyrchain(1000), PalletInstance(9)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(
|
||||
1,
|
||||
X2([Teyrchain(1000), AccountId32 { network: None, id: [1u8; 32] }].into()),
|
||||
);
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
|
||||
// Other root locations cannot alias anything.
|
||||
let origin = Location::new(1, Here);
|
||||
let target = Location::new(2, X1([GlobalConsensus(Ethereum { chain_id: 1 })].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(2, X2([GlobalConsensus(Pezkuwi), Teyrchain(1000)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(0, X2([PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
|
||||
let origin = Location::new(0, Here);
|
||||
let target = Location::new(1, X1([Teyrchain(2000)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(1001)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(1002)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn authorized_cross_chain_aliases() {
|
||||
// origin and target are different accounts on different chains
|
||||
let origin: AccountId = [100; 32].into();
|
||||
let bad_origin: AccountId = [150; 32].into();
|
||||
let target: AccountId = [200; 32].into();
|
||||
let fees = ZAGROS_ED * 10;
|
||||
|
||||
let pal_admin = <PenpalB as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get());
|
||||
PenpalB::mint_foreign_asset(pal_admin.clone(), Location::parent(), origin.clone(), fees * 10);
|
||||
PenpalB::mint_foreign_asset(pal_admin, Location::parent(), bad_origin.clone(), fees * 10);
|
||||
CoretimeZagros::fund_accounts(vec![(target.clone(), fees * 10)]);
|
||||
|
||||
// let's authorize `origin` on Penpal to alias `target` on Coretime
|
||||
CoretimeZagros::execute_with(|| {
|
||||
let penpal_origin = Location::new(
|
||||
1,
|
||||
X2([
|
||||
Teyrchain(PenpalB::para_id().into()),
|
||||
AccountId32 {
|
||||
network: Some(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
id: origin.clone().into(),
|
||||
},
|
||||
]
|
||||
.into()),
|
||||
);
|
||||
// `target` adds `penpal_origin` as authorized alias
|
||||
assert_ok!(<CoretimeZagros as CoretimeZagrosPallet>::PezkuwiXcm::add_authorized_alias(
|
||||
<CoretimeZagros as Chain>::RuntimeOrigin::signed(target.clone()),
|
||||
Box::new(penpal_origin.into()),
|
||||
None
|
||||
));
|
||||
});
|
||||
// Verify that unauthorized `bad_origin` cannot alias into `target`, from any chain.
|
||||
test_cross_chain_alias!(
|
||||
vec![
|
||||
// between AH and Coretime: denied
|
||||
(AssetHubZagros, CoretimeZagros, TELEPORT_FEES, DENIED),
|
||||
// between BH and Coretime: denied
|
||||
(BridgeHubZagros, CoretimeZagros, TELEPORT_FEES, DENIED),
|
||||
// between People and Coretime: denied
|
||||
(PeopleZagros, CoretimeZagros, TELEPORT_FEES, DENIED),
|
||||
// between Penpal and Coretime: denied
|
||||
(PenpalB, CoretimeZagros, RESERVE_TRANSFER_FEES, DENIED)
|
||||
],
|
||||
bad_origin,
|
||||
target,
|
||||
fees
|
||||
);
|
||||
// Verify that only authorized `penpal::origin` can alias into `target`, while `origin` on other
|
||||
// chains cannot.
|
||||
test_cross_chain_alias!(
|
||||
vec![
|
||||
// between AH and Coretime: denied
|
||||
(AssetHubZagros, CoretimeZagros, TELEPORT_FEES, DENIED),
|
||||
// between BH and Coretime: denied
|
||||
(BridgeHubZagros, CoretimeZagros, TELEPORT_FEES, DENIED),
|
||||
// between People and Coretime: denied
|
||||
(PeopleZagros, CoretimeZagros, TELEPORT_FEES, DENIED),
|
||||
// between Penpal and Coretime: allowed
|
||||
(PenpalB, CoretimeZagros, RESERVE_TRANSFER_FEES, ALLOWED)
|
||||
],
|
||||
origin,
|
||||
target,
|
||||
fees
|
||||
);
|
||||
// remove authorization for `origin` on Penpal to alias `target` on Coretime
|
||||
CoretimeZagros::execute_with(|| {
|
||||
// `target` removes all authorized aliases
|
||||
assert_ok!(
|
||||
<CoretimeZagros as CoretimeZagrosPallet>::PezkuwiXcm::remove_all_authorized_aliases(
|
||||
<CoretimeZagros as Chain>::RuntimeOrigin::signed(target.clone())
|
||||
)
|
||||
);
|
||||
});
|
||||
// Verify `penpal::origin` can no longer alias into `target` on Coretime.
|
||||
test_cross_chain_alias!(
|
||||
vec![(PenpalB, CoretimeZagros, RESERVE_TRANSFER_FEES, DENIED)],
|
||||
origin,
|
||||
target,
|
||||
fees
|
||||
);
|
||||
}
|
||||
+34
@@ -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 = CoretimeZagrosExistentialDeposit::get();
|
||||
let assets: Assets = (Parent, amount).into();
|
||||
|
||||
test_chain_can_claim_assets!(
|
||||
CoretimeZagros,
|
||||
RuntimeCall,
|
||||
NetworkId::ByGenesis(ZAGROS_GENESIS_HASH),
|
||||
assets,
|
||||
amount
|
||||
);
|
||||
}
|
||||
+228
@@ -0,0 +1,228 @@
|
||||
// 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::traits::OnInitialize;
|
||||
use pezpallet_broker::{ConfigRecord, Configuration, CoreAssignment, CoreMask, ScheduleItem};
|
||||
use pezsp_runtime::Perbill;
|
||||
use zagros_runtime_constants::system_teyrchain::coretime::TIMESLICE_PERIOD;
|
||||
use zagros_system_emulated_network::zagros_emulated_chain::zagros_runtime::Dmp;
|
||||
|
||||
#[test]
|
||||
fn transact_hardcoded_weights_are_sane() {
|
||||
// There are three transacts with hardcoded weights sent from the Coretime Chain to the Relay
|
||||
// Chain across the CoretimeInterface which are triggered at various points in the sales cycle.
|
||||
// - Request core count - triggered directly by `start_sales` or `request_core_count`
|
||||
// extrinsics.
|
||||
// - Request revenue info - triggered when each timeslice is committed.
|
||||
// - Assign core - triggered when an entry is encountered in the workplan for the next
|
||||
// timeslice.
|
||||
|
||||
// RuntimeEvent aliases to avoid warning from usage of qualified paths in assertions due to
|
||||
// <https://github.com/rust-lang/rust/issues/86935>
|
||||
type CoretimeEvent = <CoretimeZagros as Chain>::RuntimeEvent;
|
||||
type RelayEvent = <Zagros as Chain>::RuntimeEvent;
|
||||
|
||||
Zagros::execute_with(|| {
|
||||
Dmp::make_teyrchain_reachable(CoretimeZagros::para_id());
|
||||
});
|
||||
|
||||
// Reserve a workload, configure broker and start sales.
|
||||
CoretimeZagros::execute_with(|| {
|
||||
// Hooks don't run in emulated tests - workaround as we need `on_initialize` to tick things
|
||||
// along and have no concept of time passing otherwise.
|
||||
<CoretimeZagros as CoretimeZagrosPallet>::Broker::on_initialize(
|
||||
<CoretimeZagros as Chain>::System::block_number(),
|
||||
);
|
||||
|
||||
let coretime_root_origin = <CoretimeZagros as Chain>::RuntimeOrigin::root();
|
||||
|
||||
// Create and populate schedule with the worst case assignment on this core.
|
||||
let mut schedule = Vec::new();
|
||||
for i in 0..80 {
|
||||
schedule.push(ScheduleItem {
|
||||
mask: CoreMask::void().set(i),
|
||||
assignment: CoreAssignment::Task(2000 + i),
|
||||
})
|
||||
}
|
||||
|
||||
assert_ok!(<CoretimeZagros as CoretimeZagrosPallet>::Broker::reserve(
|
||||
coretime_root_origin.clone(),
|
||||
schedule.try_into().expect("Vector is within bounds."),
|
||||
));
|
||||
|
||||
// Configure broker and start sales.
|
||||
let config = ConfigRecord {
|
||||
advance_notice: 1,
|
||||
interlude_length: 1,
|
||||
leadin_length: 2,
|
||||
region_length: 1,
|
||||
ideal_bulk_proportion: Perbill::from_percent(40),
|
||||
limit_cores_offered: None,
|
||||
renewal_bump: Perbill::from_percent(2),
|
||||
contribution_timeout: 1,
|
||||
};
|
||||
assert_ok!(<CoretimeZagros as CoretimeZagrosPallet>::Broker::configure(
|
||||
coretime_root_origin.clone(),
|
||||
config
|
||||
));
|
||||
assert_ok!(<CoretimeZagros as CoretimeZagrosPallet>::Broker::start_sales(
|
||||
coretime_root_origin,
|
||||
100,
|
||||
0
|
||||
));
|
||||
assert_eq!(
|
||||
pezpallet_broker::Status::<<CoretimeZagros as Chain>::Runtime>::get()
|
||||
.unwrap()
|
||||
.core_count,
|
||||
1
|
||||
);
|
||||
|
||||
assert_expected_events!(
|
||||
CoretimeZagros,
|
||||
vec![
|
||||
CoretimeEvent::Broker(
|
||||
pezpallet_broker::Event::ReservationMade { .. }
|
||||
) => {},
|
||||
CoretimeEvent::Broker(
|
||||
pezpallet_broker::Event::CoreCountRequested { core_count: 1 }
|
||||
) => {},
|
||||
CoretimeEvent::TeyrchainSystem(
|
||||
cumulus_pallet_teyrchain_system::Event::UpwardMessageSent { .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
// Check that the request_core_count message was processed successfully. This will fail if the
|
||||
// weights are misconfigured.
|
||||
Zagros::execute_with(|| {
|
||||
Zagros::assert_ump_queue_processed(true, Some(CoretimeZagros::para_id()), None);
|
||||
|
||||
assert_expected_events!(
|
||||
Zagros,
|
||||
vec![
|
||||
RelayEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
// Keep track of the relay chain block number so we can fast forward while still checking the
|
||||
// right block.
|
||||
let mut block_number_cursor = Zagros::ext_wrapper(<Zagros as Chain>::System::block_number);
|
||||
|
||||
let config = CoretimeZagros::ext_wrapper(|| {
|
||||
Configuration::<<CoretimeZagros as Chain>::Runtime>::get()
|
||||
.expect("Pallet was configured earlier.")
|
||||
});
|
||||
|
||||
// Now run up to the block before the sale is rotated.
|
||||
while block_number_cursor < TIMESLICE_PERIOD - config.advance_notice - 1 {
|
||||
CoretimeZagros::execute_with(|| {
|
||||
// Hooks don't run in emulated tests - workaround.
|
||||
<CoretimeZagros as CoretimeZagrosPallet>::Broker::on_initialize(
|
||||
<CoretimeZagros as Chain>::System::block_number(),
|
||||
);
|
||||
});
|
||||
|
||||
Zagros::ext_wrapper(|| {
|
||||
block_number_cursor = <Zagros as Chain>::System::block_number();
|
||||
});
|
||||
}
|
||||
|
||||
// In this block we trigger assign core.
|
||||
CoretimeZagros::execute_with(|| {
|
||||
// Hooks don't run in emulated tests - workaround.
|
||||
<CoretimeZagros as CoretimeZagrosPallet>::Broker::on_initialize(
|
||||
<CoretimeZagros as Chain>::System::block_number(),
|
||||
);
|
||||
|
||||
assert_expected_events!(
|
||||
CoretimeZagros,
|
||||
vec![
|
||||
CoretimeEvent::Broker(
|
||||
pezpallet_broker::Event::SaleInitialized { .. }
|
||||
) => {},
|
||||
CoretimeEvent::Broker(
|
||||
pezpallet_broker::Event::CoreAssigned { .. }
|
||||
) => {},
|
||||
CoretimeEvent::TeyrchainSystem(
|
||||
cumulus_pallet_teyrchain_system::Event::UpwardMessageSent { .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
// In this block we trigger request revenue.
|
||||
CoretimeZagros::execute_with(|| {
|
||||
// Hooks don't run in emulated tests - workaround.
|
||||
<CoretimeZagros as CoretimeZagrosPallet>::Broker::on_initialize(
|
||||
<CoretimeZagros as Chain>::System::block_number(),
|
||||
);
|
||||
|
||||
assert_expected_events!(
|
||||
CoretimeZagros,
|
||||
vec![
|
||||
CoretimeEvent::TeyrchainSystem(
|
||||
cumulus_pallet_teyrchain_system::Event::UpwardMessageSent { .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
// Check that the assign_core and request_revenue_info_at messages were processed successfully.
|
||||
// This will fail if the weights are misconfigured.
|
||||
Zagros::execute_with(|| {
|
||||
Zagros::assert_ump_queue_processed(true, Some(CoretimeZagros::para_id()), None);
|
||||
|
||||
assert_expected_events!(
|
||||
Zagros,
|
||||
vec![
|
||||
RelayEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
RelayEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
RelayEvent::Coretime(
|
||||
pezkuwi_runtime_teyrchains::coretime::Event::CoreAssigned { .. }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
// Here we receive and process the notify_revenue XCM with zero revenue.
|
||||
CoretimeZagros::execute_with(|| {
|
||||
// Hooks don't run in emulated tests - workaround.
|
||||
<CoretimeZagros as CoretimeZagrosPallet>::Broker::on_initialize(
|
||||
<CoretimeZagros as Chain>::System::block_number(),
|
||||
);
|
||||
|
||||
assert_expected_events!(
|
||||
CoretimeZagros,
|
||||
vec![
|
||||
CoretimeEvent::MessageQueue(
|
||||
pezpallet_message_queue::Event::Processed { success: true, .. }
|
||||
) => {},
|
||||
// Zero revenue in first timeslice so history is immediately dropped.
|
||||
CoretimeEvent::Broker(
|
||||
pezpallet_broker::Event::HistoryDropped { when: 0, revenue: 0 }
|
||||
) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// 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 aliases;
|
||||
mod claim_assets;
|
||||
mod coretime_interface;
|
||||
mod teleport;
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
// 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::{
|
||||
test_relay_is_trusted_teleporter, test_teyrchain_is_trusted_teleporter,
|
||||
test_teyrchain_is_trusted_teleporter_for_relay,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn teleport_via_limited_teleport_assets_from_and_to_relay() {
|
||||
let amount = ZAGROS_ED * 10;
|
||||
|
||||
test_relay_is_trusted_teleporter!(
|
||||
Zagros, // Origin
|
||||
vec![CoretimeZagros], // Destinations
|
||||
amount,
|
||||
limited_teleport_assets
|
||||
);
|
||||
|
||||
test_teyrchain_is_trusted_teleporter_for_relay!(
|
||||
CoretimeZagros, // Origin
|
||||
Zagros, // Destination
|
||||
amount,
|
||||
limited_teleport_assets
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleport_via_transfer_assets_from_and_to_relay() {
|
||||
let amount = ZAGROS_ED * 10;
|
||||
|
||||
test_relay_is_trusted_teleporter!(
|
||||
Zagros, // Origin
|
||||
vec![CoretimeZagros], // Destinations
|
||||
amount,
|
||||
transfer_assets
|
||||
);
|
||||
|
||||
test_teyrchain_is_trusted_teleporter_for_relay!(
|
||||
CoretimeZagros, // Origin
|
||||
Zagros, // Destination
|
||||
amount,
|
||||
transfer_assets
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleport_via_limited_teleport_assets_from_coretime_to_asset_hub() {
|
||||
let amount = ASSET_HUB_ZAGROS_ED * 100;
|
||||
let native_asset: Assets = (Parent, amount).into();
|
||||
|
||||
let fee_asset_id: AssetId = Parent.into();
|
||||
test_teyrchain_is_trusted_teleporter!(
|
||||
CoretimeZagros, // Origin
|
||||
vec![AssetHubZagros], // Destinations
|
||||
(native_asset, amount),
|
||||
fee_asset_id,
|
||||
limited_teleport_assets
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleport_via_transfer_assets_from_coretime_to_asset_hub() {
|
||||
let amount = ASSET_HUB_ZAGROS_ED * 100;
|
||||
let native_asset: Assets = (Parent, amount).into();
|
||||
|
||||
let fee_asset_id: AssetId = Parent.into();
|
||||
test_teyrchain_is_trusted_teleporter!(
|
||||
CoretimeZagros, // Origin
|
||||
vec![AssetHubZagros], // Destinations
|
||||
(native_asset, amount),
|
||||
fee_asset_id,
|
||||
transfer_assets
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleport_via_limited_teleport_assets_from_asset_hub_to_coretime() {
|
||||
let amount = CORETIME_ZAGROS_ED * 100;
|
||||
let native_asset: Assets = (Parent, amount).into();
|
||||
|
||||
let fee_asset_id: AssetId = Parent.into();
|
||||
test_teyrchain_is_trusted_teleporter!(
|
||||
AssetHubZagros, // Origin
|
||||
vec![CoretimeZagros], // Destinations
|
||||
(native_asset, amount),
|
||||
fee_asset_id,
|
||||
limited_teleport_assets
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleport_via_transfer_assets_from_asset_hub_to_coretime() {
|
||||
let amount = CORETIME_ZAGROS_ED * 100;
|
||||
let native_asset: Assets = (Parent, amount).into();
|
||||
|
||||
let fee_asset_id: AssetId = Parent.into();
|
||||
test_teyrchain_is_trusted_teleporter!(
|
||||
AssetHubZagros, // Origin
|
||||
vec![CoretimeZagros], // Destinations
|
||||
(native_asset, amount),
|
||||
fee_asset_id,
|
||||
transfer_assets
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
[package]
|
||||
name = "governance-zagros-integration-tests"
|
||||
version = "0.0.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license = "Apache-2.0"
|
||||
description = "Zagros governance integration tests with xcm-emulator"
|
||||
publish = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
codec = { workspace = true, default-features = true }
|
||||
|
||||
# Bizinikiwi
|
||||
pezframe-support = { workspace = true, default-features = true }
|
||||
pezframe-system = { workspace = true, default-features = true }
|
||||
pezpallet-utility = { workspace = true, default-features = true }
|
||||
pezpallet-whitelist = { workspace = true, default-features = true }
|
||||
pezsp-core = { workspace = true, default-features = true }
|
||||
pezsp-runtime = { workspace = true, default-features = true }
|
||||
|
||||
pezpallet-xcm = { workspace = true, default-features = true }
|
||||
xcm = { workspace = true, default-features = true }
|
||||
|
||||
emulated-integration-tests-common = { workspace = true }
|
||||
|
||||
# Local
|
||||
asset-hub-zagros-runtime = { workspace = true }
|
||||
collectives-zagros-runtime = { workspace = true }
|
||||
zagros-runtime = { workspace = true }
|
||||
zagros-system-emulated-network = { workspace = true }
|
||||
|
||||
[features]
|
||||
runtime-benchmarks = [
|
||||
"asset-hub-zagros-runtime/runtime-benchmarks",
|
||||
"collectives-zagros-runtime/runtime-benchmarks",
|
||||
"emulated-integration-tests-common/runtime-benchmarks",
|
||||
"pezframe-support/runtime-benchmarks",
|
||||
"pezframe-system/runtime-benchmarks",
|
||||
"pezpallet-utility/runtime-benchmarks",
|
||||
"pezpallet-whitelist/runtime-benchmarks",
|
||||
"pezpallet-xcm/runtime-benchmarks",
|
||||
"pezsp-runtime/runtime-benchmarks",
|
||||
"xcm/runtime-benchmarks",
|
||||
"zagros-runtime/runtime-benchmarks",
|
||||
"zagros-system-emulated-network/runtime-benchmarks",
|
||||
]
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
// Copyright (C) Parity Technologies and the various Pezkuwi contributors, see Contributions.md
|
||||
// for a list of specific contributors.
|
||||
// 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::*;
|
||||
|
||||
/// CollectivesZagros dispatches `pezpallet_xcm::send` with `OriginKind:Xcm` to the dest with encoded
|
||||
/// whitelist call.
|
||||
#[cfg(test)]
|
||||
pub fn collectives_send_whitelist(
|
||||
dest: Location,
|
||||
encoded_whitelist_call: impl FnOnce() -> Vec<u8>,
|
||||
) {
|
||||
CollectivesZagros::execute_with(|| {
|
||||
type RuntimeEvent = <CollectivesZagros as Chain>::RuntimeEvent;
|
||||
type RuntimeCall = <CollectivesZagros as Chain>::RuntimeCall;
|
||||
type RuntimeOrigin = <CollectivesZagros as Chain>::RuntimeOrigin;
|
||||
type Runtime = <CollectivesZagros as Chain>::Runtime;
|
||||
|
||||
let send_whitelist_call = RuntimeCall::PezkuwiXcm(pezpallet_xcm::Call::<Runtime>::send {
|
||||
dest: bx!(VersionedLocation::from(dest)),
|
||||
message: bx!(VersionedXcm::from(Xcm(vec![
|
||||
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
|
||||
Transact {
|
||||
origin_kind: OriginKind::Xcm,
|
||||
fallback_max_weight: None,
|
||||
call: encoded_whitelist_call().into(),
|
||||
}
|
||||
]))),
|
||||
});
|
||||
|
||||
use collectives_zagros_runtime::fellowship::pezpallet_fellowship_origins::Origin::Fellows as FellowsOrigin;
|
||||
let fellows_origin: RuntimeOrigin = FellowsOrigin.into();
|
||||
assert_ok!(send_whitelist_call.dispatch(fellows_origin));
|
||||
assert_expected_events!(
|
||||
CollectivesZagros,
|
||||
vec![
|
||||
RuntimeEvent::PezkuwiXcm(pezpallet_xcm::Event::Sent { .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright (C) Parity Technologies and the various Pezkuwi contributors, see Contributions.md
|
||||
// for a list of specific contributors.
|
||||
// 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;
|
||||
pub(crate) use emulated_integration_tests_common::{
|
||||
impls::{assert_expected_events, bx, TestExt},
|
||||
xcm_emulator::Chain,
|
||||
xcm_helpers::{
|
||||
build_xcm_send_authorize_upgrade_call, call_hash_of,
|
||||
dispatch_whitelisted_call_with_preimage,
|
||||
},
|
||||
};
|
||||
pub(crate) use pezframe_support::{assert_err, assert_ok};
|
||||
pub(crate) use pezsp_runtime::{traits::Dispatchable, DispatchError};
|
||||
pub(crate) use xcm::{latest::prelude::*, VersionedLocation, VersionedXcm};
|
||||
pub(crate) use zagros_system_emulated_network::CollectivesZagrosPara as CollectivesZagros;
|
||||
|
||||
pub(crate) use zagros_system_emulated_network::{
|
||||
AssetHubZagrosPara as AssetHubZagros, BridgeHubZagrosPara as BridgeHubZagros,
|
||||
CoretimeZagrosPara as CoretimeZagros, PeopleZagrosPara as PeopleZagros,
|
||||
ZagrosRelay as Zagros,
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod common;
|
||||
|
||||
#[cfg(test)]
|
||||
mod open_gov_on_relay;
|
||||
|
||||
#[cfg(test)]
|
||||
mod open_gov_on_asset_hub;
|
||||
+287
@@ -0,0 +1,287 @@
|
||||
// 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::{common::*, imports::*};
|
||||
use asset_hub_zagros_runtime::governance::pezpallet_custom_origins::Origin;
|
||||
use emulated_integration_tests_common::impls::Teyrchain;
|
||||
|
||||
#[test]
|
||||
fn assethub_can_authorize_upgrade_for_itself() {
|
||||
let code_hash = [1u8; 32].into();
|
||||
type AssetHubRuntime = <AssetHubZagros as Chain>::Runtime;
|
||||
type AssetHubRuntimeCall = <AssetHubZagros as Chain>::RuntimeCall;
|
||||
type AssetHubRuntimeOrigin = <AssetHubZagros as Chain>::RuntimeOrigin;
|
||||
|
||||
let authorize_upgrade =
|
||||
AssetHubRuntimeCall::Utility(pezpallet_utility::Call::<AssetHubRuntime>::force_batch {
|
||||
calls: vec![AssetHubRuntimeCall::System(pezframe_system::Call::authorize_upgrade {
|
||||
code_hash,
|
||||
})],
|
||||
});
|
||||
|
||||
// bad origin
|
||||
let invalid_origin: AssetHubRuntimeOrigin = Origin::StakingAdmin.into();
|
||||
// ok origin
|
||||
let ok_origin: AssetHubRuntimeOrigin = Origin::WhitelistedCaller.into();
|
||||
|
||||
// store preimage
|
||||
let call_hash = call_hash_of::<AssetHubZagros>(&authorize_upgrade);
|
||||
|
||||
// Err - when dispatch non-whitelisted
|
||||
assert_err!(
|
||||
dispatch_whitelisted_call_with_preimage::<AssetHubZagros>(
|
||||
authorize_upgrade.clone(),
|
||||
ok_origin.clone()
|
||||
),
|
||||
DispatchError::Module(pezsp_runtime::ModuleError {
|
||||
index: 93,
|
||||
error: [3, 0, 0, 0],
|
||||
message: Some("CallIsNotWhitelisted")
|
||||
})
|
||||
);
|
||||
|
||||
// whitelist
|
||||
collectives_send_whitelist(
|
||||
CollectivesZagros::sibling_location_of(<AssetHubZagros as Teyrchain>::para_id()),
|
||||
|| {
|
||||
AssetHubRuntimeCall::Whitelist(
|
||||
pezpallet_whitelist::Call::<AssetHubRuntime>::whitelist_call { call_hash },
|
||||
)
|
||||
.encode()
|
||||
},
|
||||
);
|
||||
|
||||
// Err - when dispatch wrong origin
|
||||
assert_err!(
|
||||
dispatch_whitelisted_call_with_preimage::<AssetHubZagros>(
|
||||
authorize_upgrade.clone(),
|
||||
invalid_origin
|
||||
),
|
||||
DispatchError::BadOrigin
|
||||
);
|
||||
|
||||
// check before
|
||||
AssetHubZagros::execute_with(|| {
|
||||
assert!(<AssetHubZagros as Chain>::System::authorized_upgrade().is_none())
|
||||
});
|
||||
|
||||
// ok - authorized
|
||||
assert_ok!(dispatch_whitelisted_call_with_preimage::<AssetHubZagros>(
|
||||
authorize_upgrade,
|
||||
ok_origin
|
||||
));
|
||||
|
||||
// check after - authorized
|
||||
AssetHubZagros::execute_with(|| {
|
||||
assert_eq!(
|
||||
<AssetHubZagros as Chain>::System::authorized_upgrade().unwrap().code_hash(),
|
||||
&code_hash
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assethub_can_authorize_upgrade_for_relay_chain() {
|
||||
let code_hash = [1u8; 32].into();
|
||||
type AssetHubRuntime = <AssetHubZagros as Chain>::Runtime;
|
||||
type AssetHubRuntimeCall = <AssetHubZagros as Chain>::RuntimeCall;
|
||||
type AssetHubRuntimeOrigin = <AssetHubZagros as Chain>::RuntimeOrigin;
|
||||
|
||||
let authorize_upgrade =
|
||||
AssetHubRuntimeCall::Utility(pezpallet_utility::Call::<AssetHubRuntime>::force_batch {
|
||||
calls: vec![build_xcm_send_authorize_upgrade_call::<AssetHubZagros, Zagros>(
|
||||
AssetHubZagros::parent_location(),
|
||||
&code_hash,
|
||||
None,
|
||||
)],
|
||||
});
|
||||
|
||||
// bad origin
|
||||
let invalid_origin: AssetHubRuntimeOrigin = Origin::StakingAdmin.into();
|
||||
// ok origin
|
||||
let ok_origin: AssetHubRuntimeOrigin = Origin::WhitelistedCaller.into();
|
||||
|
||||
let call_hash = call_hash_of::<AssetHubZagros>(&authorize_upgrade);
|
||||
|
||||
// Err - when dispatch non-whitelisted
|
||||
assert_err!(
|
||||
dispatch_whitelisted_call_with_preimage::<AssetHubZagros>(
|
||||
authorize_upgrade.clone(),
|
||||
ok_origin.clone()
|
||||
),
|
||||
DispatchError::Module(pezsp_runtime::ModuleError {
|
||||
index: 93,
|
||||
error: [3, 0, 0, 0],
|
||||
message: Some("CallIsNotWhitelisted")
|
||||
})
|
||||
);
|
||||
|
||||
// whitelist
|
||||
collectives_send_whitelist(
|
||||
CollectivesZagros::sibling_location_of(<AssetHubZagros as Teyrchain>::para_id()),
|
||||
|| {
|
||||
AssetHubRuntimeCall::Whitelist(
|
||||
pezpallet_whitelist::Call::<AssetHubRuntime>::whitelist_call { call_hash },
|
||||
)
|
||||
.encode()
|
||||
},
|
||||
);
|
||||
|
||||
// Err - when dispatch wrong origin
|
||||
assert_err!(
|
||||
dispatch_whitelisted_call_with_preimage::<AssetHubZagros>(
|
||||
authorize_upgrade.clone(),
|
||||
invalid_origin
|
||||
),
|
||||
DispatchError::BadOrigin
|
||||
);
|
||||
|
||||
// check before
|
||||
Zagros::execute_with(|| assert!(<Zagros as Chain>::System::authorized_upgrade().is_none()));
|
||||
|
||||
// ok - authorized
|
||||
assert_ok!(dispatch_whitelisted_call_with_preimage::<AssetHubZagros>(
|
||||
authorize_upgrade,
|
||||
ok_origin
|
||||
));
|
||||
|
||||
// check after - authorized
|
||||
Zagros::execute_with(|| {
|
||||
assert_eq!(<Zagros as Chain>::System::authorized_upgrade().unwrap().code_hash(), &code_hash)
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assethub_can_authorize_upgrade_for_system_chains() {
|
||||
type AssetHubRuntime = <AssetHubZagros as Chain>::Runtime;
|
||||
type AssetHubRuntimeCall = <AssetHubZagros as Chain>::RuntimeCall;
|
||||
type AssetHubRuntimeOrigin = <AssetHubZagros as Chain>::RuntimeOrigin;
|
||||
|
||||
let code_hash_bridge_hub = [2u8; 32].into();
|
||||
let code_hash_collectives = [3u8; 32].into();
|
||||
let code_hash_coretime = [4u8; 32].into();
|
||||
let code_hash_people = [5u8; 32].into();
|
||||
|
||||
let authorize_upgrade =
|
||||
AssetHubRuntimeCall::Utility(pezpallet_utility::Call::<AssetHubRuntime>::force_batch {
|
||||
calls: vec![
|
||||
build_xcm_send_authorize_upgrade_call::<AssetHubZagros, BridgeHubZagros>(
|
||||
AssetHubZagros::sibling_location_of(BridgeHubZagros::para_id()),
|
||||
&code_hash_bridge_hub,
|
||||
None,
|
||||
),
|
||||
build_xcm_send_authorize_upgrade_call::<AssetHubZagros, CollectivesZagros>(
|
||||
AssetHubZagros::sibling_location_of(CollectivesZagros::para_id()),
|
||||
&code_hash_collectives,
|
||||
None,
|
||||
),
|
||||
build_xcm_send_authorize_upgrade_call::<AssetHubZagros, CoretimeZagros>(
|
||||
AssetHubZagros::sibling_location_of(CoretimeZagros::para_id()),
|
||||
&code_hash_coretime,
|
||||
None,
|
||||
),
|
||||
build_xcm_send_authorize_upgrade_call::<AssetHubZagros, PeopleZagros>(
|
||||
AssetHubZagros::sibling_location_of(PeopleZagros::para_id()),
|
||||
&code_hash_people,
|
||||
None,
|
||||
),
|
||||
],
|
||||
});
|
||||
|
||||
// bad origin
|
||||
let invalid_origin: AssetHubRuntimeOrigin = Origin::StakingAdmin.into();
|
||||
// ok origin
|
||||
let ok_origin: AssetHubRuntimeOrigin = Origin::WhitelistedCaller.into();
|
||||
|
||||
let call_hash = call_hash_of::<AssetHubZagros>(&authorize_upgrade);
|
||||
|
||||
// Err - when dispatch non-whitelisted
|
||||
assert_err!(
|
||||
dispatch_whitelisted_call_with_preimage::<AssetHubZagros>(
|
||||
authorize_upgrade.clone(),
|
||||
ok_origin.clone()
|
||||
),
|
||||
DispatchError::Module(pezsp_runtime::ModuleError {
|
||||
index: 93,
|
||||
error: [3, 0, 0, 0],
|
||||
message: Some("CallIsNotWhitelisted")
|
||||
})
|
||||
);
|
||||
|
||||
// whitelist
|
||||
collectives_send_whitelist(
|
||||
CollectivesZagros::sibling_location_of(<AssetHubZagros as Teyrchain>::para_id()),
|
||||
|| {
|
||||
AssetHubRuntimeCall::Whitelist(
|
||||
pezpallet_whitelist::Call::<AssetHubRuntime>::whitelist_call { call_hash },
|
||||
)
|
||||
.encode()
|
||||
},
|
||||
);
|
||||
|
||||
// Err - when dispatch wrong origin
|
||||
assert_err!(
|
||||
dispatch_whitelisted_call_with_preimage::<AssetHubZagros>(
|
||||
authorize_upgrade.clone(),
|
||||
invalid_origin
|
||||
),
|
||||
DispatchError::BadOrigin
|
||||
);
|
||||
|
||||
// check before
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
assert!(<BridgeHubZagros as Chain>::System::authorized_upgrade().is_none())
|
||||
});
|
||||
CollectivesZagros::execute_with(|| {
|
||||
assert!(<CollectivesZagros as Chain>::System::authorized_upgrade().is_none())
|
||||
});
|
||||
CoretimeZagros::execute_with(|| {
|
||||
assert!(<CoretimeZagros as Chain>::System::authorized_upgrade().is_none())
|
||||
});
|
||||
PeopleZagros::execute_with(|| {
|
||||
assert!(<PeopleZagros as Chain>::System::authorized_upgrade().is_none())
|
||||
});
|
||||
|
||||
// ok - authorized
|
||||
assert_ok!(dispatch_whitelisted_call_with_preimage::<AssetHubZagros>(
|
||||
authorize_upgrade,
|
||||
ok_origin
|
||||
));
|
||||
|
||||
// check after - authorized
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
assert_eq!(
|
||||
<BridgeHubZagros as Chain>::System::authorized_upgrade().unwrap().code_hash(),
|
||||
&code_hash_bridge_hub
|
||||
)
|
||||
});
|
||||
CollectivesZagros::execute_with(|| {
|
||||
assert_eq!(
|
||||
<CollectivesZagros as Chain>::System::authorized_upgrade().unwrap().code_hash(),
|
||||
&code_hash_collectives
|
||||
)
|
||||
});
|
||||
CoretimeZagros::execute_with(|| {
|
||||
assert_eq!(
|
||||
<CoretimeZagros as Chain>::System::authorized_upgrade().unwrap().code_hash(),
|
||||
&code_hash_coretime
|
||||
)
|
||||
});
|
||||
PeopleZagros::execute_with(|| {
|
||||
assert_eq!(
|
||||
<PeopleZagros as Chain>::System::authorized_upgrade().unwrap().code_hash(),
|
||||
&code_hash_people
|
||||
)
|
||||
});
|
||||
}
|
||||
+236
@@ -0,0 +1,236 @@
|
||||
// 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::{common::*, imports::*};
|
||||
use emulated_integration_tests_common::{
|
||||
assert_whitelisted,
|
||||
impls::RelayChain,
|
||||
xcm_emulator::{Chain, TestExt, Teyrchain},
|
||||
};
|
||||
use zagros_runtime::governance::pezpallet_custom_origins::Origin;
|
||||
use zagros_system_emulated_network::{
|
||||
AssetHubZagrosPara as AssetHubZagros, BridgeHubZagrosPara as BridgeHubZagros,
|
||||
CoretimeZagrosPara as CoretimeZagros, PeopleZagrosPara as PeopleZagros, ZagrosRelay as Zagros,
|
||||
};
|
||||
|
||||
use zagros_system_emulated_network::zagros_emulated_chain::zagros_runtime::Dmp;
|
||||
|
||||
#[test]
|
||||
fn relaychain_can_authorize_upgrade_for_itself() {
|
||||
let code_hash = [1u8; 32].into();
|
||||
type ZagrosRuntime = <Zagros as Chain>::Runtime;
|
||||
type ZagrosRuntimeCall = <Zagros as Chain>::RuntimeCall;
|
||||
type ZagrosRuntimeOrigin = <Zagros as Chain>::RuntimeOrigin;
|
||||
|
||||
let authorize_upgrade =
|
||||
ZagrosRuntimeCall::Utility(pezpallet_utility::Call::<ZagrosRuntime>::force_batch {
|
||||
calls: vec![
|
||||
// upgrade the relaychain
|
||||
ZagrosRuntimeCall::System(pezframe_system::Call::authorize_upgrade { code_hash }),
|
||||
],
|
||||
});
|
||||
|
||||
// bad origin
|
||||
let invalid_origin: ZagrosRuntimeOrigin = Origin::StakingAdmin.into();
|
||||
// ok origin
|
||||
let ok_origin: ZagrosRuntimeOrigin = Origin::WhitelistedCaller.into();
|
||||
|
||||
let call_hash = call_hash_of::<Zagros>(&authorize_upgrade);
|
||||
|
||||
// Err - when dispatch non-whitelisted
|
||||
assert_err!(
|
||||
dispatch_whitelisted_call_with_preimage::<Zagros>(
|
||||
authorize_upgrade.clone(),
|
||||
ok_origin.clone()
|
||||
),
|
||||
DispatchError::Module(pezsp_runtime::ModuleError {
|
||||
index: 36,
|
||||
error: [3, 0, 0, 0],
|
||||
message: Some("CallIsNotWhitelisted")
|
||||
})
|
||||
);
|
||||
|
||||
// whitelist
|
||||
collectives_send_whitelist(Location::parent(), || {
|
||||
ZagrosRuntimeCall::Whitelist(pezpallet_whitelist::Call::<ZagrosRuntime>::whitelist_call {
|
||||
call_hash,
|
||||
})
|
||||
.encode()
|
||||
});
|
||||
|
||||
// Err - when dispatch wrong origin
|
||||
assert_err!(
|
||||
dispatch_whitelisted_call_with_preimage::<Zagros>(
|
||||
authorize_upgrade.clone(),
|
||||
invalid_origin
|
||||
),
|
||||
DispatchError::BadOrigin
|
||||
);
|
||||
|
||||
// check before
|
||||
Zagros::execute_with(|| assert!(<Zagros as Chain>::System::authorized_upgrade().is_none()));
|
||||
|
||||
// ok - authorized
|
||||
assert_ok!(dispatch_whitelisted_call_with_preimage::<Zagros>(authorize_upgrade, ok_origin));
|
||||
|
||||
// check after - authorized
|
||||
Zagros::execute_with(|| {
|
||||
assert_eq!(<Zagros as Chain>::System::authorized_upgrade().unwrap().code_hash(), &code_hash)
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn relaychain_can_authorize_upgrade_for_system_chains() {
|
||||
type ZagrosRuntime = <Zagros as Chain>::Runtime;
|
||||
type ZagrosRuntimeCall = <Zagros as Chain>::RuntimeCall;
|
||||
type ZagrosRuntimeOrigin = <Zagros as Chain>::RuntimeOrigin;
|
||||
|
||||
Zagros::execute_with(|| {
|
||||
Dmp::make_teyrchain_reachable(AssetHubZagros::para_id());
|
||||
Dmp::make_teyrchain_reachable(BridgeHubZagros::para_id());
|
||||
Dmp::make_teyrchain_reachable(CollectivesZagros::para_id());
|
||||
Dmp::make_teyrchain_reachable(CoretimeZagros::para_id());
|
||||
Dmp::make_teyrchain_reachable(PeopleZagros::para_id());
|
||||
});
|
||||
|
||||
let code_hash_asset_hub = [1u8; 32].into();
|
||||
let code_hash_bridge_hub = [2u8; 32].into();
|
||||
let code_hash_collectives = [3u8; 32].into();
|
||||
let code_hash_coretime = [4u8; 32].into();
|
||||
let code_hash_people = [5u8; 32].into();
|
||||
|
||||
let authorize_upgrade =
|
||||
ZagrosRuntimeCall::Utility(pezpallet_utility::Call::<ZagrosRuntime>::force_batch {
|
||||
calls: vec![
|
||||
build_xcm_send_authorize_upgrade_call::<Zagros, AssetHubZagros>(
|
||||
Zagros::child_location_of(AssetHubZagros::para_id()),
|
||||
&code_hash_asset_hub,
|
||||
None,
|
||||
),
|
||||
build_xcm_send_authorize_upgrade_call::<Zagros, BridgeHubZagros>(
|
||||
Zagros::child_location_of(BridgeHubZagros::para_id()),
|
||||
&code_hash_bridge_hub,
|
||||
None,
|
||||
),
|
||||
build_xcm_send_authorize_upgrade_call::<Zagros, CollectivesZagros>(
|
||||
Zagros::child_location_of(CollectivesZagros::para_id()),
|
||||
&code_hash_collectives,
|
||||
None,
|
||||
),
|
||||
build_xcm_send_authorize_upgrade_call::<Zagros, CoretimeZagros>(
|
||||
Zagros::child_location_of(CoretimeZagros::para_id()),
|
||||
&code_hash_coretime,
|
||||
None,
|
||||
),
|
||||
build_xcm_send_authorize_upgrade_call::<Zagros, PeopleZagros>(
|
||||
Zagros::child_location_of(PeopleZagros::para_id()),
|
||||
&code_hash_people,
|
||||
None,
|
||||
),
|
||||
],
|
||||
});
|
||||
|
||||
// bad origin
|
||||
let invalid_origin: ZagrosRuntimeOrigin = Origin::StakingAdmin.into();
|
||||
// ok origin
|
||||
let ok_origin: ZagrosRuntimeOrigin = Origin::WhitelistedCaller.into();
|
||||
|
||||
let call_hash = call_hash_of::<Zagros>(&authorize_upgrade);
|
||||
|
||||
// Err - when dispatch non-whitelisted
|
||||
assert_err!(
|
||||
dispatch_whitelisted_call_with_preimage::<Zagros>(
|
||||
authorize_upgrade.clone(),
|
||||
ok_origin.clone()
|
||||
),
|
||||
DispatchError::Module(pezsp_runtime::ModuleError {
|
||||
index: 36,
|
||||
error: [3, 0, 0, 0],
|
||||
message: Some("CallIsNotWhitelisted")
|
||||
})
|
||||
);
|
||||
|
||||
// whitelist
|
||||
collectives_send_whitelist(Location::parent(), || {
|
||||
ZagrosRuntimeCall::Whitelist(pezpallet_whitelist::Call::<ZagrosRuntime>::whitelist_call {
|
||||
call_hash,
|
||||
})
|
||||
.encode()
|
||||
});
|
||||
|
||||
Zagros::execute_with(|| {
|
||||
assert_whitelisted!(Zagros, call_hash);
|
||||
});
|
||||
|
||||
// Err - when dispatch wrong origin
|
||||
assert_err!(
|
||||
dispatch_whitelisted_call_with_preimage::<Zagros>(
|
||||
authorize_upgrade.clone(),
|
||||
invalid_origin
|
||||
),
|
||||
DispatchError::BadOrigin
|
||||
);
|
||||
|
||||
// check before
|
||||
AssetHubZagros::execute_with(|| {
|
||||
assert!(<AssetHubZagros as Chain>::System::authorized_upgrade().is_none())
|
||||
});
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
assert!(<BridgeHubZagros as Chain>::System::authorized_upgrade().is_none())
|
||||
});
|
||||
CollectivesZagros::execute_with(|| {
|
||||
assert!(<CollectivesZagros as Chain>::System::authorized_upgrade().is_none())
|
||||
});
|
||||
CoretimeZagros::execute_with(|| {
|
||||
assert!(<CoretimeZagros as Chain>::System::authorized_upgrade().is_none())
|
||||
});
|
||||
PeopleZagros::execute_with(|| {
|
||||
assert!(<PeopleZagros as Chain>::System::authorized_upgrade().is_none())
|
||||
});
|
||||
|
||||
// ok - authorized
|
||||
assert_ok!(dispatch_whitelisted_call_with_preimage::<Zagros>(authorize_upgrade, ok_origin));
|
||||
|
||||
AssetHubZagros::execute_with(|| {
|
||||
assert_eq!(
|
||||
<AssetHubZagros as Chain>::System::authorized_upgrade().unwrap().code_hash(),
|
||||
&code_hash_asset_hub
|
||||
)
|
||||
});
|
||||
// check after - authorized
|
||||
BridgeHubZagros::execute_with(|| {
|
||||
assert_eq!(
|
||||
<BridgeHubZagros as Chain>::System::authorized_upgrade().unwrap().code_hash(),
|
||||
&code_hash_bridge_hub
|
||||
)
|
||||
});
|
||||
CollectivesZagros::execute_with(|| {
|
||||
assert_eq!(
|
||||
<CollectivesZagros as Chain>::System::authorized_upgrade().unwrap().code_hash(),
|
||||
&code_hash_collectives
|
||||
)
|
||||
});
|
||||
CoretimeZagros::execute_with(|| {
|
||||
assert_eq!(
|
||||
<CoretimeZagros as Chain>::System::authorized_upgrade().unwrap().code_hash(),
|
||||
&code_hash_coretime
|
||||
)
|
||||
});
|
||||
PeopleZagros::execute_with(|| {
|
||||
assert_eq!(
|
||||
<PeopleZagros as Chain>::System::authorized_upgrade().unwrap().code_hash(),
|
||||
&code_hash_people
|
||||
)
|
||||
});
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
[package]
|
||||
name = "people-pezkuwichain-integration-tests"
|
||||
version = "0.1.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license = "Apache-2.0"
|
||||
description = "People Pezkuwichain runtime integration tests with xcm-emulator"
|
||||
publish = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
# Bizinikiwi
|
||||
pezframe-support = { workspace = true }
|
||||
pezpallet-balances = { workspace = true }
|
||||
pezsp-runtime = { workspace = true }
|
||||
|
||||
# Pezkuwi
|
||||
xcm = { workspace = true }
|
||||
xcm-executor = { workspace = true }
|
||||
|
||||
# Pezcumulus
|
||||
asset-test-utils = { workspace = true, default-features = 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",
|
||||
"emulated-integration-tests-common/runtime-benchmarks",
|
||||
"pezframe-support/runtime-benchmarks",
|
||||
"pezpallet-balances/runtime-benchmarks",
|
||||
"pezkuwichain-system-emulated-network/runtime-benchmarks",
|
||||
"pezsp-runtime/runtime-benchmarks",
|
||||
"teyrchains-common/runtime-benchmarks",
|
||||
"xcm-executor/runtime-benchmarks",
|
||||
"xcm/runtime-benchmarks",
|
||||
]
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
// 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 {
|
||||
// Bizinikiwi
|
||||
pub(crate) use pezframe_support::pezsp_runtime::DispatchResult;
|
||||
|
||||
// Pezkuwi
|
||||
pub(crate) use xcm::{latest::PEZKUWICHAIN_GENESIS_HASH, prelude::*};
|
||||
|
||||
// Pezcumulus
|
||||
pub(crate) use asset_test_utils::xcm_helpers;
|
||||
pub(crate) use emulated_integration_tests_common::xcm_emulator::{
|
||||
assert_expected_events, bx, Chain, Test, TestArgs, TestContext, TestExt, Teyrchain as Para,
|
||||
};
|
||||
pub(crate) use pezkuwichain_system_emulated_network::{
|
||||
people_pezkuwichain_emulated_chain::{
|
||||
people_pezkuwichain_runtime::{
|
||||
xcm_config::XcmConfig as PeoplePezkuwichainXcmConfig,
|
||||
ExistentialDeposit as PeoplePezkuwichainExistentialDeposit,
|
||||
},
|
||||
PeoplePezkuwichainParaPallet as PeoplePezkuwichainPallet,
|
||||
},
|
||||
pezkuwichain_emulated_chain::{
|
||||
genesis::ED as PEZKUWICHAIN_ED, PezkuwichainRelayPallet as PezkuwichainPallet,
|
||||
},
|
||||
AssetHubPezkuwichainPara as AssetHubPezkuwichain,
|
||||
AssetHubPezkuwichainParaReceiver as AssetHubPezkuwichainReceiver,
|
||||
PeoplePezkuwichainPara as PeoplePezkuwichain,
|
||||
PeoplePezkuwichainParaReceiver as PeoplePezkuwichainReceiver,
|
||||
PeoplePezkuwichainParaSender as PeoplePezkuwichainSender,
|
||||
PezkuwichainRelay as Pezkuwichain, PezkuwichainRelayReceiver as PezkuwichainReceiver,
|
||||
PezkuwichainRelaySender as PezkuwichainSender,
|
||||
};
|
||||
pub(crate) use teyrchains_common::Balance;
|
||||
|
||||
pub(crate) type SystemParaToRelayTest = Test<PeoplePezkuwichain, Pezkuwichain>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
+34
@@ -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 = PeoplePezkuwichainExistentialDeposit::get();
|
||||
let assets: Assets = (Parent, amount).into();
|
||||
|
||||
test_chain_can_claim_assets!(
|
||||
PeoplePezkuwichain,
|
||||
RuntimeCall,
|
||||
NetworkId::ByGenesis(PEZKUWICHAIN_GENESIS_HASH),
|
||||
assets,
|
||||
amount
|
||||
);
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// 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 teleport;
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
// 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::{
|
||||
test_relay_is_trusted_teleporter, test_teyrchain_is_trusted_teleporter,
|
||||
test_teyrchain_is_trusted_teleporter_for_relay,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn teleport_via_limited_teleport_assets_from_and_to_relay() {
|
||||
let amount = PEZKUWICHAIN_ED * 100;
|
||||
|
||||
test_relay_is_trusted_teleporter!(
|
||||
Pezkuwichain,
|
||||
vec![PeoplePezkuwichain],
|
||||
amount,
|
||||
limited_teleport_assets
|
||||
);
|
||||
|
||||
test_teyrchain_is_trusted_teleporter_for_relay!(
|
||||
PeoplePezkuwichain,
|
||||
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![PeoplePezkuwichain],
|
||||
amount,
|
||||
transfer_assets
|
||||
);
|
||||
|
||||
test_teyrchain_is_trusted_teleporter_for_relay!(
|
||||
PeoplePezkuwichain,
|
||||
Pezkuwichain,
|
||||
amount,
|
||||
transfer_assets
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleport_via_limited_teleport_assets_to_other_system_teyrchains_works() {
|
||||
let amount = PEZKUWICHAIN_ED * 100;
|
||||
let native_asset: Assets = (Parent, amount).into();
|
||||
|
||||
let fee_asset_id: AssetId = Parent.into();
|
||||
test_teyrchain_is_trusted_teleporter!(
|
||||
PeoplePezkuwichain, // Origin
|
||||
vec![AssetHubPezkuwichain], // Destinations
|
||||
(native_asset, amount),
|
||||
fee_asset_id,
|
||||
limited_teleport_assets
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleport_via_transfer_assets_to_other_system_teyrchains_works() {
|
||||
let amount = PEZKUWICHAIN_ED * 100;
|
||||
let native_asset: Assets = (Parent, amount).into();
|
||||
|
||||
let fee_asset_id: AssetId = Parent.into();
|
||||
test_teyrchain_is_trusted_teleporter!(
|
||||
PeoplePezkuwichain, // Origin
|
||||
vec![AssetHubPezkuwichain], // Destinations
|
||||
(native_asset, amount),
|
||||
fee_asset_id,
|
||||
transfer_assets
|
||||
);
|
||||
}
|
||||
|
||||
fn relay_dest_assertions_fail(_t: SystemParaToRelayTest) {
|
||||
Pezkuwichain::assert_ump_queue_processed(false, Some(PeoplePezkuwichain::para_id()), None);
|
||||
}
|
||||
|
||||
fn para_origin_assertions(t: SystemParaToRelayTest) {
|
||||
type RuntimeEvent = <PeoplePezkuwichain as Chain>::RuntimeEvent;
|
||||
|
||||
PeoplePezkuwichain::assert_xcm_pallet_attempted_complete(None);
|
||||
|
||||
PeoplePezkuwichain::assert_teyrchain_system_ump_sent();
|
||||
|
||||
assert_expected_events!(
|
||||
PeoplePezkuwichain,
|
||||
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 system_para_limited_teleport_assets(t: SystemParaToRelayTest) -> DispatchResult {
|
||||
<PeoplePezkuwichain as PeoplePezkuwichainPallet>::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,
|
||||
)
|
||||
}
|
||||
|
||||
/// 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 = PEZKUWICHAIN_ED * 1000;
|
||||
let destination = PeoplePezkuwichain::parent_location();
|
||||
let beneficiary_id = PezkuwichainReceiver::get();
|
||||
let assets = (Parent, amount_to_send).into();
|
||||
let fee_asset_id: AssetId = Parent.into();
|
||||
|
||||
// Fund a sender
|
||||
PeoplePezkuwichain::fund_accounts(vec![(
|
||||
PeoplePezkuwichainSender::get(),
|
||||
PEZKUWICHAIN_ED * 2_000u128,
|
||||
)]);
|
||||
|
||||
let test_args = TestContext {
|
||||
sender: PeoplePezkuwichainSender::get(),
|
||||
receiver: PezkuwichainReceiver::get(),
|
||||
args: TestArgs::new_para(
|
||||
destination,
|
||||
beneficiary_id,
|
||||
amount_to_send,
|
||||
assets,
|
||||
None,
|
||||
fee_asset_id,
|
||||
),
|
||||
};
|
||||
|
||||
let mut test = SystemParaToRelayTest::new(test_args);
|
||||
|
||||
let sender_balance_before = test.sender.balance;
|
||||
let receiver_balance_before = test.receiver.balance;
|
||||
|
||||
test.set_assertion::<PeoplePezkuwichain>(para_origin_assertions);
|
||||
test.set_assertion::<Pezkuwichain>(relay_dest_assertions_fail);
|
||||
test.set_dispatchable::<PeoplePezkuwichain>(system_para_limited_teleport_assets);
|
||||
test.assert();
|
||||
|
||||
let sender_balance_after = test.sender.balance;
|
||||
let receiver_balance_after = test.receiver.balance;
|
||||
|
||||
let delivery_fees = PeoplePezkuwichain::execute_with(|| {
|
||||
xcm_helpers::teleport_assets_delivery_fees::<
|
||||
<PeoplePezkuwichainXcmConfig as xcm_executor::Config>::XcmSender,
|
||||
>(
|
||||
test.args.assets.clone(),
|
||||
test.args.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);
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
[package]
|
||||
name = "people-zagros-integration-tests"
|
||||
version = "0.1.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license = "Apache-2.0"
|
||||
description = "People Zagros runtime integration tests with xcm-emulator"
|
||||
publish = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
codec = { workspace = true }
|
||||
|
||||
# Bizinikiwi
|
||||
pezframe-support = { workspace = true }
|
||||
pezpallet-balances = { workspace = true }
|
||||
pezpallet-identity = { workspace = true }
|
||||
pezpallet-message-queue = { workspace = true }
|
||||
pezpallet-xcm = { workspace = true }
|
||||
pezsp-runtime = { workspace = true }
|
||||
|
||||
# Pezkuwi
|
||||
xcm = { workspace = true }
|
||||
xcm-executor = { workspace = true }
|
||||
zagros-runtime = { workspace = true }
|
||||
|
||||
# Pezcumulus
|
||||
emulated-integration-tests-common = { workspace = true }
|
||||
teyrchains-common = { workspace = true, default-features = true }
|
||||
zagros-system-emulated-network = { workspace = true }
|
||||
|
||||
[features]
|
||||
runtime-benchmarks = [
|
||||
"emulated-integration-tests-common/runtime-benchmarks",
|
||||
"pezframe-support/runtime-benchmarks",
|
||||
"pezpallet-balances/runtime-benchmarks",
|
||||
"pezpallet-identity/runtime-benchmarks",
|
||||
"pezpallet-message-queue/runtime-benchmarks",
|
||||
"pezpallet-xcm/runtime-benchmarks",
|
||||
"pezsp-runtime/runtime-benchmarks",
|
||||
"teyrchains-common/runtime-benchmarks",
|
||||
"xcm-executor/runtime-benchmarks",
|
||||
"xcm/runtime-benchmarks",
|
||||
"zagros-runtime/runtime-benchmarks",
|
||||
"zagros-system-emulated-network/runtime-benchmarks",
|
||||
]
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
// 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 {
|
||||
// Bizinikiwi
|
||||
pub(crate) use pezframe_support::assert_ok;
|
||||
|
||||
// Pezkuwi
|
||||
pub(crate) use xcm::{latest::ZAGROS_GENESIS_HASH, prelude::*};
|
||||
|
||||
// Pezcumulus
|
||||
pub(crate) use emulated_integration_tests_common::xcm_emulator::{
|
||||
assert_expected_events, bx, Chain, TestExt, Teyrchain as Para,
|
||||
};
|
||||
pub(crate) use zagros_system_emulated_network::{
|
||||
self,
|
||||
asset_hub_zagros_emulated_chain::AssetHubZagrosParaPallet as AssetHubZagrosPallet,
|
||||
bridge_hub_zagros_emulated_chain::BridgeHubZagrosParaPallet as BridgeHubZagrosPallet,
|
||||
collectives_zagros_emulated_chain::CollectivesZagrosParaPallet as CollectivesZagrosPallet,
|
||||
coretime_zagros_emulated_chain::CoretimeZagrosParaPallet as CoretimeZagrosPallet,
|
||||
penpal_emulated_chain::{PenpalAssetOwner, PenpalBParaPallet as PenpalBPallet},
|
||||
people_zagros_emulated_chain::{
|
||||
people_zagros_runtime::{
|
||||
self, xcm_config::XcmConfig as PeopleZagrosXcmConfig,
|
||||
ExistentialDeposit as PeopleZagrosExistentialDeposit,
|
||||
},
|
||||
PeopleZagrosParaPallet as PeopleZagrosPallet,
|
||||
},
|
||||
zagros_emulated_chain::{genesis::ED as ZAGROS_ED, ZagrosRelayPallet as ZagrosPallet},
|
||||
AssetHubZagrosPara as AssetHubZagros, AssetHubZagrosParaReceiver as AssetHubZagrosReceiver,
|
||||
BridgeHubZagrosPara as BridgeHubZagros, CollectivesZagrosPara as CollectivesZagros,
|
||||
CoretimeZagrosPara as CoretimeZagros, PenpalBPara as PenpalB,
|
||||
PeopleZagrosPara as PeopleZagros, PeopleZagrosParaReceiver as PeopleZagrosReceiver,
|
||||
PeopleZagrosParaSender as PeopleZagrosSender, ZagrosRelay as Zagros,
|
||||
ZagrosRelayReceiver as ZagrosReceiver, ZagrosRelaySender as ZagrosSender,
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
+273
@@ -0,0 +1,273 @@
|
||||
// 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 XCM aliasing.
|
||||
|
||||
use crate::imports::*;
|
||||
use emulated_integration_tests_common::{macros::AccountId, test_cross_chain_alias};
|
||||
use pezframe_support::traits::ContainsPair;
|
||||
use xcm::latest::Junctions::*;
|
||||
|
||||
const ALLOWED: bool = true;
|
||||
const DENIED: bool = false;
|
||||
|
||||
const TELEPORT_FEES: bool = true;
|
||||
const RESERVE_TRANSFER_FEES: bool = false;
|
||||
|
||||
#[test]
|
||||
fn account_on_sibling_syschain_aliases_into_same_local_account() {
|
||||
// origin and target are the same account on different chains
|
||||
let origin: AccountId = [1; 32].into();
|
||||
let target = origin.clone();
|
||||
let fees = ZAGROS_ED * 10;
|
||||
|
||||
PenpalB::mint_foreign_asset(
|
||||
<PenpalB as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get()),
|
||||
Location::parent(),
|
||||
origin.clone(),
|
||||
fees * 10,
|
||||
);
|
||||
|
||||
// Aliasing same account on different chains
|
||||
test_cross_chain_alias!(
|
||||
vec![
|
||||
// between AH and People: allowed
|
||||
(AssetHubZagros, PeopleZagros, TELEPORT_FEES, ALLOWED),
|
||||
// between BH and People: allowed
|
||||
(BridgeHubZagros, PeopleZagros, TELEPORT_FEES, ALLOWED),
|
||||
// between Collectives and People: allowed
|
||||
(CollectivesZagros, PeopleZagros, TELEPORT_FEES, ALLOWED),
|
||||
// between Coretime and People: allowed
|
||||
(CoretimeZagros, PeopleZagros, TELEPORT_FEES, ALLOWED),
|
||||
// between Penpal and People: denied
|
||||
(PenpalB, PeopleZagros, RESERVE_TRANSFER_FEES, DENIED)
|
||||
],
|
||||
origin,
|
||||
target,
|
||||
fees
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn account_on_sibling_syschain_cannot_alias_into_different_local_account() {
|
||||
// origin and target are different accounts on different chains
|
||||
let origin: AccountId = [1; 32].into();
|
||||
let target: AccountId = [2; 32].into();
|
||||
let fees = ZAGROS_ED * 10;
|
||||
|
||||
PenpalB::mint_foreign_asset(
|
||||
<PenpalB as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get()),
|
||||
Location::parent(),
|
||||
origin.clone(),
|
||||
fees * 10,
|
||||
);
|
||||
|
||||
// Aliasing different account on different chains
|
||||
test_cross_chain_alias!(
|
||||
vec![
|
||||
// between AH and People: denied
|
||||
(AssetHubZagros, PeopleZagros, TELEPORT_FEES, DENIED),
|
||||
// between BH and People: denied
|
||||
(BridgeHubZagros, PeopleZagros, TELEPORT_FEES, DENIED),
|
||||
// between Collectives and People: denied
|
||||
(CollectivesZagros, PeopleZagros, TELEPORT_FEES, DENIED),
|
||||
// between Coretime and People: denied
|
||||
(CoretimeZagros, PeopleZagros, TELEPORT_FEES, DENIED),
|
||||
// between Penpal and People: denied
|
||||
(PenpalB, PeopleZagros, RESERVE_TRANSFER_FEES, DENIED)
|
||||
],
|
||||
origin,
|
||||
target,
|
||||
fees
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aliasing_child_locations() {
|
||||
use PeopleZagrosXcmConfig as XcmConfig;
|
||||
PeopleZagros::execute_with(|| {
|
||||
// Allows aliasing descendant of origin.
|
||||
let origin = Location::new(1, X1([PalletInstance(8)].into()));
|
||||
let target = Location::new(1, X2([PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(8)].into()));
|
||||
let target = Location::new(
|
||||
1,
|
||||
X2([Teyrchain(8), AccountId32 { network: None, id: [1u8; 32] }].into()),
|
||||
);
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(8)].into()));
|
||||
let target =
|
||||
Location::new(1, X3([Teyrchain(8), PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
|
||||
// Does not allow if not descendant.
|
||||
let origin = Location::new(1, X1([PalletInstance(8)].into()));
|
||||
let target = Location::new(0, X2([PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(8)].into()));
|
||||
let target = Location::new(
|
||||
0,
|
||||
X2([Teyrchain(8), AccountId32 { network: None, id: [1u8; 32] }].into()),
|
||||
);
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(8)].into()));
|
||||
let target = Location::new(0, X1([AccountId32 { network: None, id: [1u8; 32] }].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([AccountId32 { network: None, id: [1u8; 32] }].into()));
|
||||
let target = Location::new(0, X1([AccountId32 { network: None, id: [1u8; 32] }].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn asset_hub_root_aliases_anything() {
|
||||
use PeopleZagrosXcmConfig as XcmConfig;
|
||||
PeopleZagros::execute_with(|| {
|
||||
// Allows AH root to alias anything.
|
||||
let origin = Location::new(1, X1([Teyrchain(1000)].into()));
|
||||
|
||||
let target = Location::new(1, X1([Teyrchain(2000)].into()));
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(1, X1([AccountId32 { network: None, id: [1u8; 32] }].into()));
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(
|
||||
1,
|
||||
X2([Teyrchain(8), AccountId32 { network: None, id: [1u8; 32] }].into()),
|
||||
);
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target =
|
||||
Location::new(1, X3([Teyrchain(42), PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(2, X1([GlobalConsensus(Ethereum { chain_id: 1 })].into()));
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(2, X2([GlobalConsensus(Pezkuwi), Teyrchain(1000)].into()));
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(0, X2([PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
|
||||
// Other AH locations cannot alias anything.
|
||||
let origin = Location::new(1, X2([Teyrchain(1000), GeneralIndex(9)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X2([Teyrchain(1000), PalletInstance(9)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(
|
||||
1,
|
||||
X2([Teyrchain(1000), AccountId32 { network: None, id: [1u8; 32] }].into()),
|
||||
);
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
|
||||
// Other root locations cannot alias anything.
|
||||
let origin = Location::new(1, Here);
|
||||
let target = Location::new(2, X1([GlobalConsensus(Ethereum { chain_id: 1 })].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(2, X2([GlobalConsensus(Pezkuwi), Teyrchain(1000)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let target = Location::new(0, X2([PalletInstance(8), GeneralIndex(9)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
|
||||
let origin = Location::new(0, Here);
|
||||
let target = Location::new(1, X1([Teyrchain(2000)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(1001)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
let origin = Location::new(1, X1([Teyrchain(1002)].into()));
|
||||
assert!(!<XcmConfig as xcm_executor::Config>::Aliasers::contains(&origin, &target));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn authorized_cross_chain_aliases() {
|
||||
// origin and target are different accounts on different chains
|
||||
let origin: AccountId = [100; 32].into();
|
||||
let bad_origin: AccountId = [150; 32].into();
|
||||
let target: AccountId = [200; 32].into();
|
||||
let fees = ZAGROS_ED * 10;
|
||||
|
||||
let pal_admin = <PenpalB as Chain>::RuntimeOrigin::signed(PenpalAssetOwner::get());
|
||||
PenpalB::mint_foreign_asset(pal_admin.clone(), Location::parent(), origin.clone(), fees * 10);
|
||||
PenpalB::mint_foreign_asset(pal_admin, Location::parent(), bad_origin.clone(), fees * 10);
|
||||
PeopleZagros::fund_accounts(vec![(target.clone(), fees * 10)]);
|
||||
|
||||
// let's authorize `origin` on Penpal to alias `target` on People
|
||||
PeopleZagros::execute_with(|| {
|
||||
let penpal_origin = Location::new(
|
||||
1,
|
||||
X2([
|
||||
Teyrchain(PenpalB::para_id().into()),
|
||||
AccountId32 {
|
||||
network: Some(ByGenesis(ZAGROS_GENESIS_HASH)),
|
||||
id: origin.clone().into(),
|
||||
},
|
||||
]
|
||||
.into()),
|
||||
);
|
||||
// `target` adds `penpal_origin` as authorized alias
|
||||
assert_ok!(<PeopleZagros as PeopleZagrosPallet>::PezkuwiXcm::add_authorized_alias(
|
||||
<PeopleZagros as Chain>::RuntimeOrigin::signed(target.clone()),
|
||||
Box::new(penpal_origin.into()),
|
||||
None
|
||||
));
|
||||
});
|
||||
// Verify that unauthorized `bad_origin` cannot alias into `target`, from any chain.
|
||||
test_cross_chain_alias!(
|
||||
vec![
|
||||
// between AH and People: denied
|
||||
(AssetHubZagros, PeopleZagros, TELEPORT_FEES, DENIED),
|
||||
// between BH and People: denied
|
||||
(BridgeHubZagros, PeopleZagros, TELEPORT_FEES, DENIED),
|
||||
// between Collectives and People: denied
|
||||
(CollectivesZagros, PeopleZagros, TELEPORT_FEES, DENIED),
|
||||
// between Penpal and People: denied
|
||||
(PenpalB, PeopleZagros, RESERVE_TRANSFER_FEES, DENIED)
|
||||
],
|
||||
bad_origin,
|
||||
target,
|
||||
fees
|
||||
);
|
||||
// Verify that only authorized `penpal::origin` can alias into `target`, while `origin` on other
|
||||
// chains cannot.
|
||||
test_cross_chain_alias!(
|
||||
vec![
|
||||
// between AH and People: denied
|
||||
(AssetHubZagros, PeopleZagros, TELEPORT_FEES, DENIED),
|
||||
// between BH and People: denied
|
||||
(BridgeHubZagros, PeopleZagros, TELEPORT_FEES, DENIED),
|
||||
// between Collectives and People: denied
|
||||
(CollectivesZagros, PeopleZagros, TELEPORT_FEES, DENIED),
|
||||
// between Penpal and People: allowed
|
||||
(PenpalB, PeopleZagros, RESERVE_TRANSFER_FEES, ALLOWED)
|
||||
],
|
||||
origin,
|
||||
target,
|
||||
fees
|
||||
);
|
||||
// remove authorization for `origin` on Penpal to alias `target` on People
|
||||
PeopleZagros::execute_with(|| {
|
||||
// `target` removes all authorized aliases
|
||||
assert_ok!(
|
||||
<PeopleZagros as PeopleZagrosPallet>::PezkuwiXcm::remove_all_authorized_aliases(
|
||||
<PeopleZagros as Chain>::RuntimeOrigin::signed(target.clone())
|
||||
)
|
||||
);
|
||||
});
|
||||
// Verify `penpal::origin` can no longer alias into `target` on People.
|
||||
test_cross_chain_alias!(
|
||||
vec![(PenpalB, PeopleZagros, RESERVE_TRANSFER_FEES, DENIED)],
|
||||
origin,
|
||||
target,
|
||||
fees
|
||||
);
|
||||
}
|
||||
+34
@@ -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 = PeopleZagrosExistentialDeposit::get();
|
||||
let assets: Assets = (Parent, amount).into();
|
||||
|
||||
test_chain_can_claim_assets!(
|
||||
PeopleZagros,
|
||||
RuntimeCall,
|
||||
NetworkId::ByGenesis(ZAGROS_GENESIS_HASH),
|
||||
assets,
|
||||
amount
|
||||
);
|
||||
}
|
||||
+547
@@ -0,0 +1,547 @@
|
||||
// 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 codec::Encode;
|
||||
use pezframe_support::pezsp_runtime::traits::Dispatchable;
|
||||
use people_zagros_runtime::people::IdentityInfo;
|
||||
use teyrchains_common::AccountId;
|
||||
use zagros_runtime::{
|
||||
governance::pezpallet_custom_origins::Origin::GeneralAdmin as GeneralAdminOrigin, Dmp,
|
||||
};
|
||||
use zagros_system_emulated_network::people_zagros_emulated_chain::people_zagros_runtime;
|
||||
|
||||
use pezpallet_identity::Data;
|
||||
|
||||
use emulated_integration_tests_common::accounts::{ALICE, BOB};
|
||||
|
||||
#[test]
|
||||
fn relay_commands_add_registrar() {
|
||||
let (origin_kind, origin) = (OriginKind::Superuser, <Zagros as Chain>::RuntimeOrigin::root());
|
||||
|
||||
let registrar: AccountId = [1; 32].into();
|
||||
Zagros::execute_with(|| {
|
||||
type Runtime = <Zagros as Chain>::Runtime;
|
||||
type RuntimeCall = <Zagros as Chain>::RuntimeCall;
|
||||
type RuntimeEvent = <Zagros as Chain>::RuntimeEvent;
|
||||
type PeopleCall = <PeopleZagros as Chain>::RuntimeCall;
|
||||
type PeopleRuntime = <PeopleZagros as Chain>::Runtime;
|
||||
|
||||
Dmp::make_teyrchain_reachable(1004);
|
||||
|
||||
let add_registrar_call =
|
||||
PeopleCall::Identity(pezpallet_identity::Call::<PeopleRuntime>::add_registrar {
|
||||
account: registrar.into(),
|
||||
});
|
||||
|
||||
let xcm_message = RuntimeCall::XcmPallet(pezpallet_xcm::Call::<Runtime>::send {
|
||||
dest: bx!(VersionedLocation::from(Location::new(0, [Teyrchain(1004)]))),
|
||||
message: bx!(VersionedXcm::from(Xcm(vec![
|
||||
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
|
||||
Transact {
|
||||
origin_kind,
|
||||
call: add_registrar_call.encode().into(),
|
||||
fallback_max_weight: None
|
||||
}
|
||||
]))),
|
||||
});
|
||||
|
||||
assert_ok!(xcm_message.dispatch(origin));
|
||||
|
||||
assert_expected_events!(
|
||||
Zagros,
|
||||
vec![
|
||||
RuntimeEvent::XcmPallet(pezpallet_xcm::Event::Sent { .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
PeopleZagros::execute_with(|| {
|
||||
type RuntimeEvent = <PeopleZagros as Chain>::RuntimeEvent;
|
||||
|
||||
assert_expected_events!(
|
||||
PeopleZagros,
|
||||
vec![
|
||||
RuntimeEvent::Identity(pezpallet_identity::Event::RegistrarAdded { .. }) => {},
|
||||
RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed { success: true, .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn relay_commands_add_registrar_wrong_origin() {
|
||||
let people_zagros_alice = PeopleZagros::account_id_of(ALICE);
|
||||
|
||||
let origins = vec![
|
||||
(
|
||||
OriginKind::SovereignAccount,
|
||||
<Zagros as Chain>::RuntimeOrigin::signed(people_zagros_alice),
|
||||
),
|
||||
(OriginKind::Xcm, GeneralAdminOrigin.into()),
|
||||
];
|
||||
|
||||
let mut signed_origin = true;
|
||||
|
||||
for (origin_kind, origin) in origins {
|
||||
let registrar: AccountId = [1; 32].into();
|
||||
Zagros::execute_with(|| {
|
||||
type Runtime = <Zagros as Chain>::Runtime;
|
||||
type RuntimeCall = <Zagros as Chain>::RuntimeCall;
|
||||
type RuntimeEvent = <Zagros as Chain>::RuntimeEvent;
|
||||
type PeopleCall = <PeopleZagros as Chain>::RuntimeCall;
|
||||
type PeopleRuntime = <PeopleZagros as Chain>::Runtime;
|
||||
|
||||
Dmp::make_teyrchain_reachable(1004);
|
||||
|
||||
let add_registrar_call =
|
||||
PeopleCall::Identity(pezpallet_identity::Call::<PeopleRuntime>::add_registrar {
|
||||
account: registrar.into(),
|
||||
});
|
||||
|
||||
let xcm_message = RuntimeCall::XcmPallet(pezpallet_xcm::Call::<Runtime>::send {
|
||||
dest: bx!(VersionedLocation::from(Location::new(0, [Teyrchain(1004)]))),
|
||||
message: bx!(VersionedXcm::from(Xcm(vec![
|
||||
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
|
||||
Transact {
|
||||
origin_kind,
|
||||
call: add_registrar_call.encode().into(),
|
||||
fallback_max_weight: None
|
||||
}
|
||||
]))),
|
||||
});
|
||||
|
||||
assert_ok!(xcm_message.dispatch(origin));
|
||||
assert_expected_events!(
|
||||
Zagros,
|
||||
vec![
|
||||
RuntimeEvent::XcmPallet(pezpallet_xcm::Event::Sent { .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
PeopleZagros::execute_with(|| {
|
||||
type RuntimeEvent = <PeopleZagros as Chain>::RuntimeEvent;
|
||||
|
||||
if signed_origin {
|
||||
assert_expected_events!(
|
||||
PeopleZagros,
|
||||
vec![
|
||||
RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed { success: false, .. }) => {},
|
||||
]
|
||||
);
|
||||
} else {
|
||||
assert_expected_events!(
|
||||
PeopleZagros,
|
||||
vec![
|
||||
RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed { success: true, .. }) => {},
|
||||
]
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
signed_origin = false;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn relay_commands_kill_identity() {
|
||||
// To kill an identity, first one must be set
|
||||
PeopleZagros::execute_with(|| {
|
||||
type PeopleRuntime = <PeopleZagros as Chain>::Runtime;
|
||||
type PeopleRuntimeEvent = <PeopleZagros as Chain>::RuntimeEvent;
|
||||
|
||||
let people_zagros_alice =
|
||||
<PeopleZagros as Chain>::RuntimeOrigin::signed(PeopleZagros::account_id_of(ALICE));
|
||||
|
||||
let identity_info = IdentityInfo {
|
||||
email: Data::Raw(b"test@test.io".to_vec().try_into().unwrap()),
|
||||
..Default::default()
|
||||
};
|
||||
let identity: Box<<PeopleRuntime as pezpallet_identity::Config>::IdentityInformation> =
|
||||
Box::new(identity_info);
|
||||
|
||||
assert_ok!(<PeopleZagros as PeopleZagrosPallet>::Identity::set_identity(
|
||||
people_zagros_alice,
|
||||
identity
|
||||
));
|
||||
|
||||
assert_expected_events!(
|
||||
PeopleZagros,
|
||||
vec![
|
||||
PeopleRuntimeEvent::Identity(pezpallet_identity::Event::IdentitySet { .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
let (origin_kind, origin) = (OriginKind::Superuser, <Zagros as Chain>::RuntimeOrigin::root());
|
||||
|
||||
Zagros::execute_with(|| {
|
||||
type Runtime = <Zagros as Chain>::Runtime;
|
||||
type RuntimeCall = <Zagros as Chain>::RuntimeCall;
|
||||
type PeopleCall = <PeopleZagros as Chain>::RuntimeCall;
|
||||
type RuntimeEvent = <Zagros as Chain>::RuntimeEvent;
|
||||
type PeopleRuntime = <PeopleZagros as Chain>::Runtime;
|
||||
|
||||
Dmp::make_teyrchain_reachable(1004);
|
||||
|
||||
let kill_identity_call =
|
||||
PeopleCall::Identity(pezpallet_identity::Call::<PeopleRuntime>::kill_identity {
|
||||
target: people_zagros_runtime::MultiAddress::Id(PeopleZagros::account_id_of(ALICE)),
|
||||
});
|
||||
|
||||
let xcm_message = RuntimeCall::XcmPallet(pezpallet_xcm::Call::<Runtime>::send {
|
||||
dest: bx!(VersionedLocation::from(Location::new(0, [Teyrchain(1004)]))),
|
||||
message: bx!(VersionedXcm::from(Xcm(vec![
|
||||
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
|
||||
Transact {
|
||||
origin_kind,
|
||||
call: kill_identity_call.encode().into(),
|
||||
fallback_max_weight: None
|
||||
}
|
||||
]))),
|
||||
});
|
||||
|
||||
assert_ok!(xcm_message.dispatch(origin));
|
||||
|
||||
assert_expected_events!(
|
||||
Zagros,
|
||||
vec![
|
||||
RuntimeEvent::XcmPallet(pezpallet_xcm::Event::Sent { .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
PeopleZagros::execute_with(|| {
|
||||
type RuntimeEvent = <PeopleZagros as Chain>::RuntimeEvent;
|
||||
|
||||
assert_expected_events!(
|
||||
PeopleZagros,
|
||||
vec![
|
||||
RuntimeEvent::Identity(pezpallet_identity::Event::IdentityKilled { .. }) => {},
|
||||
RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed { success: true, .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn relay_commands_kill_identity_wrong_origin() {
|
||||
let people_zagros_alice = PeopleZagros::account_id_of(BOB);
|
||||
|
||||
let origins = vec![
|
||||
(
|
||||
OriginKind::SovereignAccount,
|
||||
<Zagros as Chain>::RuntimeOrigin::signed(people_zagros_alice),
|
||||
),
|
||||
(OriginKind::Xcm, GeneralAdminOrigin.into()),
|
||||
];
|
||||
|
||||
for (origin_kind, origin) in origins {
|
||||
Zagros::execute_with(|| {
|
||||
type Runtime = <Zagros as Chain>::Runtime;
|
||||
type RuntimeCall = <Zagros as Chain>::RuntimeCall;
|
||||
type PeopleCall = <PeopleZagros as Chain>::RuntimeCall;
|
||||
type RuntimeEvent = <Zagros as Chain>::RuntimeEvent;
|
||||
type PeopleRuntime = <PeopleZagros as Chain>::Runtime;
|
||||
|
||||
Dmp::make_teyrchain_reachable(1004);
|
||||
|
||||
let kill_identity_call =
|
||||
PeopleCall::Identity(pezpallet_identity::Call::<PeopleRuntime>::kill_identity {
|
||||
target: people_zagros_runtime::MultiAddress::Id(PeopleZagros::account_id_of(
|
||||
ALICE,
|
||||
)),
|
||||
});
|
||||
|
||||
let xcm_message = RuntimeCall::XcmPallet(pezpallet_xcm::Call::<Runtime>::send {
|
||||
dest: bx!(VersionedLocation::from(Location::new(0, [Teyrchain(1004)]))),
|
||||
message: bx!(VersionedXcm::from(Xcm(vec![
|
||||
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
|
||||
Transact {
|
||||
origin_kind,
|
||||
call: kill_identity_call.encode().into(),
|
||||
fallback_max_weight: None
|
||||
}
|
||||
]))),
|
||||
});
|
||||
|
||||
assert_ok!(xcm_message.dispatch(origin));
|
||||
assert_expected_events!(
|
||||
Zagros,
|
||||
vec![
|
||||
RuntimeEvent::XcmPallet(pezpallet_xcm::Event::Sent { .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
PeopleZagros::execute_with(|| {
|
||||
assert_expected_events!(PeopleZagros, vec![]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn relay_commands_add_remove_username_authority() {
|
||||
let people_zagros_alice = PeopleZagros::account_id_of(ALICE);
|
||||
let people_zagros_bob = PeopleZagros::account_id_of(BOB);
|
||||
|
||||
let (origin_kind, origin, usr) =
|
||||
(OriginKind::Superuser, <Zagros as Chain>::RuntimeOrigin::root(), "rootusername");
|
||||
|
||||
// First, add a username authority.
|
||||
Zagros::execute_with(|| {
|
||||
type Runtime = <Zagros as Chain>::Runtime;
|
||||
type RuntimeCall = <Zagros as Chain>::RuntimeCall;
|
||||
type RuntimeEvent = <Zagros as Chain>::RuntimeEvent;
|
||||
type PeopleCall = <PeopleZagros as Chain>::RuntimeCall;
|
||||
type PeopleRuntime = <PeopleZagros as Chain>::Runtime;
|
||||
|
||||
Dmp::make_teyrchain_reachable(1004);
|
||||
|
||||
let add_username_authority =
|
||||
PeopleCall::Identity(pezpallet_identity::Call::<PeopleRuntime>::add_username_authority {
|
||||
authority: people_zagros_runtime::MultiAddress::Id(people_zagros_alice.clone()),
|
||||
suffix: b"suffix1".into(),
|
||||
allocation: 10,
|
||||
});
|
||||
|
||||
let add_authority_xcm_msg = RuntimeCall::XcmPallet(pezpallet_xcm::Call::<Runtime>::send {
|
||||
dest: bx!(VersionedLocation::from(Location::new(0, [Teyrchain(1004)]))),
|
||||
message: bx!(VersionedXcm::from(Xcm(vec![
|
||||
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
|
||||
Transact {
|
||||
origin_kind,
|
||||
call: add_username_authority.encode().into(),
|
||||
fallback_max_weight: None
|
||||
}
|
||||
]))),
|
||||
});
|
||||
|
||||
assert_ok!(add_authority_xcm_msg.dispatch(origin.clone()));
|
||||
|
||||
assert_expected_events!(
|
||||
Zagros,
|
||||
vec![
|
||||
RuntimeEvent::XcmPallet(pezpallet_xcm::Event::Sent { .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
// Check events system-teyrchain-side
|
||||
PeopleZagros::execute_with(|| {
|
||||
type RuntimeEvent = <PeopleZagros as Chain>::RuntimeEvent;
|
||||
|
||||
assert_expected_events!(
|
||||
PeopleZagros,
|
||||
vec![
|
||||
RuntimeEvent::Identity(pezpallet_identity::Event::AuthorityAdded { .. }) => {},
|
||||
RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed { success: true, .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
// Now, use the previously added username authority to concede a username to an account.
|
||||
PeopleZagros::execute_with(|| {
|
||||
type PeopleRuntimeEvent = <PeopleZagros as Chain>::RuntimeEvent;
|
||||
let full_username = [usr.to_owned(), ".suffix1".to_owned()].concat().into_bytes();
|
||||
|
||||
assert_ok!(<PeopleZagros as PeopleZagrosPallet>::Identity::set_username_for(
|
||||
<PeopleZagros as Chain>::RuntimeOrigin::signed(people_zagros_alice.clone()),
|
||||
people_zagros_runtime::MultiAddress::Id(people_zagros_bob.clone()),
|
||||
full_username,
|
||||
None,
|
||||
true
|
||||
));
|
||||
|
||||
assert_expected_events!(
|
||||
PeopleZagros,
|
||||
vec![
|
||||
PeopleRuntimeEvent::Identity(pezpallet_identity::Event::UsernameQueued { .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
// Accept the given username
|
||||
PeopleZagros::execute_with(|| {
|
||||
type PeopleRuntimeEvent = <PeopleZagros as Chain>::RuntimeEvent;
|
||||
let full_username = [usr.to_owned(), ".suffix1".to_owned()].concat().into_bytes();
|
||||
|
||||
assert_ok!(<PeopleZagros as PeopleZagrosPallet>::Identity::accept_username(
|
||||
<PeopleZagros as Chain>::RuntimeOrigin::signed(people_zagros_bob.clone()),
|
||||
full_username.try_into().unwrap(),
|
||||
));
|
||||
|
||||
assert_expected_events!(
|
||||
PeopleZagros,
|
||||
vec![
|
||||
PeopleRuntimeEvent::Identity(pezpallet_identity::Event::UsernameSet { .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
// Now, remove the username authority with another privileged XCM call.
|
||||
Zagros::execute_with(|| {
|
||||
type Runtime = <Zagros as Chain>::Runtime;
|
||||
type RuntimeCall = <Zagros as Chain>::RuntimeCall;
|
||||
type RuntimeEvent = <Zagros as Chain>::RuntimeEvent;
|
||||
type PeopleCall = <PeopleZagros as Chain>::RuntimeCall;
|
||||
type PeopleRuntime = <PeopleZagros as Chain>::Runtime;
|
||||
|
||||
Dmp::make_teyrchain_reachable(1004);
|
||||
|
||||
let remove_username_authority = PeopleCall::Identity(pezpallet_identity::Call::<
|
||||
PeopleRuntime,
|
||||
>::remove_username_authority {
|
||||
authority: people_zagros_runtime::MultiAddress::Id(people_zagros_alice.clone()),
|
||||
suffix: b"suffix1".into(),
|
||||
});
|
||||
|
||||
let remove_authority_xcm_msg = RuntimeCall::XcmPallet(pezpallet_xcm::Call::<Runtime>::send {
|
||||
dest: bx!(VersionedLocation::from(Location::new(0, [Teyrchain(1004)]))),
|
||||
message: bx!(VersionedXcm::from(Xcm(vec![
|
||||
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
|
||||
Transact {
|
||||
origin_kind,
|
||||
call: remove_username_authority.encode().into(),
|
||||
fallback_max_weight: None
|
||||
}
|
||||
]))),
|
||||
});
|
||||
|
||||
assert_ok!(remove_authority_xcm_msg.dispatch(origin));
|
||||
|
||||
assert_expected_events!(
|
||||
Zagros,
|
||||
vec![
|
||||
RuntimeEvent::XcmPallet(pezpallet_xcm::Event::Sent { .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
// Final event check.
|
||||
PeopleZagros::execute_with(|| {
|
||||
type RuntimeEvent = <PeopleZagros as Chain>::RuntimeEvent;
|
||||
|
||||
assert_expected_events!(
|
||||
PeopleZagros,
|
||||
vec![
|
||||
RuntimeEvent::Identity(pezpallet_identity::Event::AuthorityRemoved { .. }) => {},
|
||||
RuntimeEvent::MessageQueue(pezpallet_message_queue::Event::Processed { success: true, .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn relay_commands_add_remove_username_authority_wrong_origin() {
|
||||
let people_zagros_alice = PeopleZagros::account_id_of(ALICE);
|
||||
|
||||
let origins = vec![
|
||||
(
|
||||
OriginKind::SovereignAccount,
|
||||
<Zagros as Chain>::RuntimeOrigin::signed(people_zagros_alice.clone()),
|
||||
),
|
||||
(OriginKind::Xcm, GeneralAdminOrigin.into()),
|
||||
];
|
||||
|
||||
for (origin_kind, origin) in origins {
|
||||
Zagros::execute_with(|| {
|
||||
type Runtime = <Zagros as Chain>::Runtime;
|
||||
type RuntimeCall = <Zagros as Chain>::RuntimeCall;
|
||||
type RuntimeEvent = <Zagros as Chain>::RuntimeEvent;
|
||||
type PeopleCall = <PeopleZagros as Chain>::RuntimeCall;
|
||||
type PeopleRuntime = <PeopleZagros as Chain>::Runtime;
|
||||
|
||||
Dmp::make_teyrchain_reachable(1004);
|
||||
|
||||
let add_username_authority = PeopleCall::Identity(pezpallet_identity::Call::<
|
||||
PeopleRuntime,
|
||||
>::add_username_authority {
|
||||
authority: people_zagros_runtime::MultiAddress::Id(people_zagros_alice.clone()),
|
||||
suffix: b"suffix1".into(),
|
||||
allocation: 10,
|
||||
});
|
||||
|
||||
let add_authority_xcm_msg = RuntimeCall::XcmPallet(pezpallet_xcm::Call::<Runtime>::send {
|
||||
dest: bx!(VersionedLocation::from(Location::new(0, [Teyrchain(1004)]))),
|
||||
message: bx!(VersionedXcm::from(Xcm(vec![
|
||||
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
|
||||
Transact {
|
||||
origin_kind,
|
||||
call: add_username_authority.encode().into(),
|
||||
fallback_max_weight: None
|
||||
}
|
||||
]))),
|
||||
});
|
||||
|
||||
assert_ok!(add_authority_xcm_msg.dispatch(origin.clone()));
|
||||
assert_expected_events!(
|
||||
Zagros,
|
||||
vec![
|
||||
RuntimeEvent::XcmPallet(pezpallet_xcm::Event::Sent { .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
// Check events system-teyrchain-side
|
||||
PeopleZagros::execute_with(|| {
|
||||
assert_expected_events!(PeopleZagros, vec![]);
|
||||
});
|
||||
|
||||
Zagros::execute_with(|| {
|
||||
type Runtime = <Zagros as Chain>::Runtime;
|
||||
type RuntimeCall = <Zagros as Chain>::RuntimeCall;
|
||||
type RuntimeEvent = <Zagros as Chain>::RuntimeEvent;
|
||||
type PeopleCall = <PeopleZagros as Chain>::RuntimeCall;
|
||||
type PeopleRuntime = <PeopleZagros as Chain>::Runtime;
|
||||
|
||||
let remove_username_authority = PeopleCall::Identity(pezpallet_identity::Call::<
|
||||
PeopleRuntime,
|
||||
>::remove_username_authority {
|
||||
authority: people_zagros_runtime::MultiAddress::Id(people_zagros_alice.clone()),
|
||||
suffix: b"suffix1".into(),
|
||||
});
|
||||
|
||||
Dmp::make_teyrchain_reachable(1004);
|
||||
|
||||
let remove_authority_xcm_msg =
|
||||
RuntimeCall::XcmPallet(pezpallet_xcm::Call::<Runtime>::send {
|
||||
dest: bx!(VersionedLocation::from(Location::new(0, [Teyrchain(1004)]))),
|
||||
message: bx!(VersionedXcm::from(Xcm(vec![
|
||||
UnpaidExecution { weight_limit: Unlimited, check_origin: None },
|
||||
Transact {
|
||||
origin_kind: OriginKind::SovereignAccount,
|
||||
call: remove_username_authority.encode().into(),
|
||||
fallback_max_weight: None,
|
||||
}
|
||||
]))),
|
||||
});
|
||||
|
||||
assert_ok!(remove_authority_xcm_msg.dispatch(origin));
|
||||
assert_expected_events!(
|
||||
Zagros,
|
||||
vec![
|
||||
RuntimeEvent::XcmPallet(pezpallet_xcm::Event::Sent { .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
PeopleZagros::execute_with(|| {
|
||||
assert_expected_events!(PeopleZagros, vec![]);
|
||||
});
|
||||
}
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
// 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 cross-chain identity operations.
|
||||
|
||||
use crate::imports::*;
|
||||
use codec::Encode;
|
||||
use emulated_integration_tests_common::accounts::ALICE;
|
||||
use pezframe_support::BoundedVec;
|
||||
use pezpallet_identity::Data;
|
||||
use people_zagros_runtime::people::{IdentityField, IdentityInfo};
|
||||
use xcm::latest::AssetTransferFilter;
|
||||
|
||||
#[test]
|
||||
fn set_identity_cross_chain() {
|
||||
type Identity = <PeopleZagros as PeopleZagrosPallet>::Identity;
|
||||
|
||||
let asset_hub_zagros_alice = AssetHubZagros::account_id_of(ALICE);
|
||||
let people_zagros_alice = PeopleZagros::account_id_of(ALICE);
|
||||
AssetHubZagros::fund_accounts(vec![(asset_hub_zagros_alice.clone(), ZAGROS_ED * 10000)]);
|
||||
PeopleZagros::fund_accounts(vec![(people_zagros_alice.clone(), ZAGROS_ED * 10000)]);
|
||||
|
||||
PeopleZagros::execute_with(|| {
|
||||
// No identity for Alice
|
||||
assert!(!Identity::has_identity(&people_zagros_alice, IdentityField::Email as u64));
|
||||
});
|
||||
|
||||
let destination = AssetHubZagros::sibling_location_of(PeopleZagros::para_id());
|
||||
let total_fees: Asset = (Location::parent(), ZAGROS_ED * 1000).into();
|
||||
let fees: Asset = (Location::parent(), ZAGROS_ED * 500).into();
|
||||
AssetHubZagros::execute_with(|| {
|
||||
type RuntimeEvent = <AssetHubZagros as Chain>::RuntimeEvent;
|
||||
|
||||
let identity_info = IdentityInfo {
|
||||
email: Data::Raw(b"test@test.io".to_vec().try_into().unwrap()),
|
||||
..Default::default()
|
||||
};
|
||||
// Set Alice identity on People from Alice on AH
|
||||
let set_identity_call =
|
||||
<PeopleZagros as Chain>::RuntimeCall::Identity(pezpallet_identity::Call::<
|
||||
<PeopleZagros as Chain>::Runtime,
|
||||
>::set_identity {
|
||||
info: bx!(identity_info),
|
||||
});
|
||||
let xcm_message = Xcm::<()>(vec![
|
||||
WithdrawAsset(total_fees.into()),
|
||||
PayFees { asset: fees.clone() },
|
||||
InitiateTransfer {
|
||||
destination,
|
||||
remote_fees: Some(AssetTransferFilter::Teleport(fees.clone().into())),
|
||||
preserve_origin: true,
|
||||
assets: BoundedVec::new(),
|
||||
remote_xcm: Xcm(vec![
|
||||
// try to alias into `Alice` account local to People chain
|
||||
AliasOrigin(people_zagros_alice.clone().into()),
|
||||
// set identity for the local Alice account
|
||||
Transact {
|
||||
origin_kind: OriginKind::SovereignAccount,
|
||||
call: set_identity_call.encode().into(),
|
||||
fallback_max_weight: None,
|
||||
},
|
||||
RefundSurplus,
|
||||
DepositAsset {
|
||||
assets: Wild(AllCounted(1)),
|
||||
beneficiary: people_zagros_alice.clone().into(),
|
||||
},
|
||||
]),
|
||||
},
|
||||
ExpectTransactStatus(MaybeErrorCode::Success),
|
||||
RefundSurplus,
|
||||
DepositAsset {
|
||||
assets: Wild(AllCounted(1)),
|
||||
beneficiary: asset_hub_zagros_alice.clone().into(),
|
||||
},
|
||||
]);
|
||||
|
||||
let signed_origin =
|
||||
<AssetHubZagros as Chain>::RuntimeOrigin::signed(asset_hub_zagros_alice);
|
||||
assert_ok!(<AssetHubZagros as AssetHubZagrosPallet>::PezkuwiXcm::execute(
|
||||
signed_origin,
|
||||
bx!(xcm::VersionedXcm::from(xcm_message.into())),
|
||||
Weight::MAX
|
||||
));
|
||||
assert_expected_events!(
|
||||
AssetHubZagros,
|
||||
vec![
|
||||
RuntimeEvent::PezkuwiXcm(pezpallet_xcm::Event::Sent { .. }) => {},
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
PeopleZagros::execute_with(|| {
|
||||
// Verify Alice on People now has identity
|
||||
assert!(Identity::has_identity(&people_zagros_alice, IdentityField::Email as u64));
|
||||
});
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// 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 aliases;
|
||||
mod claim_assets;
|
||||
mod governance;
|
||||
mod identity;
|
||||
mod teleport;
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
// 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::{
|
||||
test_relay_is_trusted_teleporter, test_teyrchain_is_trusted_teleporter,
|
||||
test_teyrchain_is_trusted_teleporter_for_relay,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn teleport_via_limited_teleport_assets_from_and_to_relay() {
|
||||
let amount = ZAGROS_ED * 100;
|
||||
|
||||
test_relay_is_trusted_teleporter!(Zagros, vec![PeopleZagros], amount, limited_teleport_assets);
|
||||
|
||||
test_teyrchain_is_trusted_teleporter_for_relay!(
|
||||
PeopleZagros,
|
||||
Zagros,
|
||||
amount,
|
||||
limited_teleport_assets
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleport_via_transfer_assets_from_and_to_relay() {
|
||||
let amount = ZAGROS_ED * 100;
|
||||
|
||||
test_relay_is_trusted_teleporter!(Zagros, vec![PeopleZagros], amount, transfer_assets);
|
||||
|
||||
test_teyrchain_is_trusted_teleporter_for_relay!(PeopleZagros, Zagros, amount, transfer_assets);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleport_via_limited_teleport_assets_to_other_system_teyrchains_works() {
|
||||
let amount = ZAGROS_ED * 100;
|
||||
let native_asset: Assets = (Parent, amount).into();
|
||||
|
||||
let fee_asset_id: AssetId = Parent.into();
|
||||
test_teyrchain_is_trusted_teleporter!(
|
||||
PeopleZagros, // Origin
|
||||
vec![AssetHubZagros], // Destinations
|
||||
(native_asset, amount),
|
||||
fee_asset_id,
|
||||
limited_teleport_assets
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn teleport_via_transfer_assets_to_other_system_teyrchains_works() {
|
||||
let amount = ZAGROS_ED * 100;
|
||||
let native_asset: Assets = (Parent, amount).into();
|
||||
|
||||
let fee_asset_id: AssetId = Parent.into();
|
||||
test_teyrchain_is_trusted_teleporter!(
|
||||
PeopleZagros, // Origin
|
||||
vec![AssetHubZagros], // Destinations
|
||||
(native_asset, amount),
|
||||
fee_asset_id,
|
||||
transfer_assets
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user