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:
+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");
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user