mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 08:11:03 +00:00
pallet-xcm: enhance reserve_transfer_assets to support remote reserves (#1672)
## Motivation `pallet-xcm` is the main user-facing interface for XCM functionality, including assets manipulation functions like `teleportAssets()` and `reserve_transfer_assets()` calls. While `teleportAsset()` works both ways, `reserve_transfer_assets()` works only for sending reserve-based assets to a remote destination and beneficiary when the reserve is the _local chain_. ## Solution This PR enhances `pallet_xcm::(limited_)reserve_withdraw_assets` to support transfers when reserves are other chains. This will allow complete, **bi-directional** reserve-based asset transfers user stories using `pallet-xcm`. Enables following scenarios: - transferring assets with local reserve (was previously supported iff asset used as fee also had local reserve - now it works in all cases), - transferring assets with reserve on destination, - transferring assets with reserve on remote/third-party chain (iff assets and fees have same remote reserve), - transferring assets with reserve different than the reserve of the asset to be used as fees - meaning can be used to transfer random asset with local/dest reserve while using DOT for fees on all involved chains, even if DOT local/dest reserve doesn't match asset reserve, - transferring assets with any type of local/dest reserve while using fees which can be teleported between involved chains. All of the above is done by pallet inner logic without the user having to specify which scenario/reserves/teleports/etc. The correct scenario and corresponding XCM programs are identified, and respectively, built automatically based on runtime configuration of trusted teleporters and trusted reserves. #### Current limitations: - while `fees` and "non-fee" `assets` CAN have different reserves (or fees CAN be teleported), the remaining "non-fee" `assets` CANNOT, among themselves, have different reserve locations (this is also implicitly enforced by `MAX_ASSETS_FOR_TRANSFER=2`, but this can be safely increased in the future). - `fees` and "non-fee" `assets` CANNOT have **different remote** reserves (this could also be supported in the future, but adds even more complexity while possibly not being worth it - we'll see what the future holds). Fixes https://github.com/paritytech/polkadot-sdk/issues/1584 Fixes https://github.com/paritytech/polkadot-sdk/issues/2055 --------- Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com> Co-authored-by: Branislav Kontur <bkontur@gmail.com>
This commit is contained in:
@@ -962,7 +962,7 @@ mod benches {
|
||||
[cumulus_pallet_xcmp_queue, XcmpQueue]
|
||||
[cumulus_pallet_dmp_queue, DmpQueue]
|
||||
// XCM
|
||||
[pallet_xcm, PolkadotXcm]
|
||||
[pallet_xcm, PalletXcmExtrinsiscsBenchmark::<Runtime>]
|
||||
// NOTE: Make sure you point to the individual modules below.
|
||||
[pallet_xcm_benchmarks::fungible, XcmBalances]
|
||||
[pallet_xcm_benchmarks::generic, XcmGeneric]
|
||||
@@ -1200,6 +1200,7 @@ impl_runtime_apis! {
|
||||
use frame_support::traits::StorageInfoTrait;
|
||||
use frame_system_benchmarking::Pallet as SystemBench;
|
||||
use cumulus_pallet_session_benchmarking::Pallet as SessionBench;
|
||||
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark;
|
||||
|
||||
// This is defined once again in dispatch_benchmark, because list_benchmarks!
|
||||
// and add_benchmarks! are macros exported by define_benchmarks! macros and those types
|
||||
@@ -1243,6 +1244,39 @@ impl_runtime_apis! {
|
||||
use cumulus_pallet_session_benchmarking::Pallet as SessionBench;
|
||||
impl cumulus_pallet_session_benchmarking::Config for Runtime {}
|
||||
|
||||
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark;
|
||||
impl pallet_xcm::benchmarking::Config for Runtime {
|
||||
fn reachable_dest() -> Option<MultiLocation> {
|
||||
Some(Parent.into())
|
||||
}
|
||||
|
||||
fn teleportable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
|
||||
// Relay/native token can be teleported between AH and Relay.
|
||||
Some((
|
||||
MultiAsset {
|
||||
fun: Fungible(EXISTENTIAL_DEPOSIT),
|
||||
id: Concrete(Parent.into())
|
||||
},
|
||||
Parent.into(),
|
||||
))
|
||||
}
|
||||
|
||||
fn reserve_transferable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
|
||||
// AH can reserve transfer native token to some random parachain.
|
||||
let random_para_id = 43211234;
|
||||
ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests(
|
||||
random_para_id.into()
|
||||
);
|
||||
Some((
|
||||
MultiAsset {
|
||||
fun: Fungible(EXISTENTIAL_DEPOSIT),
|
||||
id: Concrete(Parent.into())
|
||||
},
|
||||
ParentThen(Parachain(random_para_id).into()).into(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
use xcm::latest::prelude::*;
|
||||
use xcm_config::{KsmLocation, MaxAssetsIntoHolding};
|
||||
use pallet_xcm_benchmarks::asset_instance_from;
|
||||
|
||||
@@ -552,11 +552,6 @@ pub type XcmRouter = WithUniqueTopic<(
|
||||
XcmpQueue,
|
||||
)>;
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
parameter_types! {
|
||||
pub ReachableDest: Option<MultiLocation> = Some(Parent.into());
|
||||
}
|
||||
|
||||
impl pallet_xcm::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
// We want to disallow users sending (arbitrary) XCMs from this chain.
|
||||
@@ -586,8 +581,6 @@ impl pallet_xcm::Config for Runtime {
|
||||
type SovereignAccountOf = LocationToAccountId;
|
||||
type MaxLockers = ConstU32<8>;
|
||||
type WeightInfo = crate::weights::pallet_xcm::WeightInfo<Runtime>;
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
type ReachableDest = ReachableDest;
|
||||
type AdminOrigin = EnsureRoot<AccountId>;
|
||||
type MaxRemoteLockConsumers = ConstU32<0>;
|
||||
type RemoteLockConsumerIdentifier = ();
|
||||
|
||||
@@ -18,13 +18,14 @@
|
||||
//! Tests for the Statemine (Kusama Assets Hub) chain.
|
||||
|
||||
use asset_hub_kusama_runtime::xcm_config::{
|
||||
AssetFeeAsExistentialDepositMultiplierFeeCharger, KsmLocation, TrustBackedAssetsPalletLocation,
|
||||
AssetFeeAsExistentialDepositMultiplierFeeCharger, KsmLocation, LocationToAccountId,
|
||||
TrustBackedAssetsPalletLocation,
|
||||
};
|
||||
pub use asset_hub_kusama_runtime::{
|
||||
xcm_config::{CheckingAccount, ForeignCreatorsSovereignAccountOf, XcmConfig},
|
||||
AllPalletsWithoutSystem, AssetDeposit, Assets, Balances, ExistentialDeposit, ForeignAssets,
|
||||
ForeignAssetsInstance, MetadataDepositBase, MetadataDepositPerByte, ParachainSystem, Runtime,
|
||||
RuntimeCall, RuntimeEvent, SessionKeys, System, TrustBackedAssetsInstance,
|
||||
RuntimeCall, RuntimeEvent, SessionKeys, System, TrustBackedAssetsInstance, XcmpQueue,
|
||||
};
|
||||
use asset_test_utils::{CollatorSessionKeys, ExtBuilder};
|
||||
use codec::{Decode, Encode};
|
||||
@@ -518,12 +519,6 @@ asset_test_utils::include_teleports_for_native_asset_works!(
|
||||
_ => None,
|
||||
}
|
||||
}),
|
||||
Box::new(|runtime_event_encoded: Vec<u8>| {
|
||||
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
|
||||
Ok(RuntimeEvent::XcmpQueue(event)) => Some(event),
|
||||
_ => None,
|
||||
}
|
||||
}),
|
||||
1000
|
||||
);
|
||||
|
||||
@@ -632,3 +627,32 @@ asset_test_utils::include_create_and_manage_foreign_assets_for_local_consensus_p
|
||||
assert_eq!(ForeignAssets::asset_ids().collect::<Vec<_>>().len(), 1);
|
||||
})
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn reserve_transfer_native_asset_to_non_teleport_para_works() {
|
||||
asset_test_utils::test_cases::reserve_transfer_native_asset_to_non_teleport_para_works::<
|
||||
Runtime,
|
||||
AllPalletsWithoutSystem,
|
||||
XcmConfig,
|
||||
ParachainSystem,
|
||||
XcmpQueue,
|
||||
LocationToAccountId,
|
||||
>(
|
||||
collator_session_keys(),
|
||||
ExistentialDeposit::get(),
|
||||
AccountId::from(ALICE),
|
||||
Box::new(|runtime_event_encoded: Vec<u8>| {
|
||||
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
|
||||
Ok(RuntimeEvent::PolkadotXcm(event)) => Some(event),
|
||||
_ => None,
|
||||
}
|
||||
}),
|
||||
Box::new(|runtime_event_encoded: Vec<u8>| {
|
||||
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
|
||||
Ok(RuntimeEvent::XcmpQueue(event)) => Some(event),
|
||||
_ => None,
|
||||
}
|
||||
}),
|
||||
WeightLimit::Unlimited,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -868,7 +868,7 @@ mod benches {
|
||||
[cumulus_pallet_xcmp_queue, XcmpQueue]
|
||||
[cumulus_pallet_dmp_queue, DmpQueue]
|
||||
// XCM
|
||||
[pallet_xcm, PolkadotXcm]
|
||||
[pallet_xcm, PalletXcmExtrinsiscsBenchmark::<Runtime>]
|
||||
// NOTE: Make sure you point to the individual modules below.
|
||||
[pallet_xcm_benchmarks::fungible, XcmBalances]
|
||||
[pallet_xcm_benchmarks::generic, XcmGeneric]
|
||||
@@ -1082,6 +1082,7 @@ impl_runtime_apis! {
|
||||
use frame_support::traits::StorageInfoTrait;
|
||||
use frame_system_benchmarking::Pallet as SystemBench;
|
||||
use cumulus_pallet_session_benchmarking::Pallet as SessionBench;
|
||||
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark;
|
||||
|
||||
// This is defined once again in dispatch_benchmark, because list_benchmarks!
|
||||
// and add_benchmarks! are macros exported by define_benchmarks! macros and those types
|
||||
@@ -1124,6 +1125,39 @@ impl_runtime_apis! {
|
||||
use cumulus_pallet_session_benchmarking::Pallet as SessionBench;
|
||||
impl cumulus_pallet_session_benchmarking::Config for Runtime {}
|
||||
|
||||
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark;
|
||||
impl pallet_xcm::benchmarking::Config for Runtime {
|
||||
fn reachable_dest() -> Option<MultiLocation> {
|
||||
Some(Parent.into())
|
||||
}
|
||||
|
||||
fn teleportable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
|
||||
// Relay/native token can be teleported between AH and Relay.
|
||||
Some((
|
||||
MultiAsset {
|
||||
fun: Fungible(EXISTENTIAL_DEPOSIT),
|
||||
id: Concrete(Parent.into())
|
||||
},
|
||||
Parent.into(),
|
||||
))
|
||||
}
|
||||
|
||||
fn reserve_transferable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
|
||||
// AH can reserve transfer native token to some random parachain.
|
||||
let random_para_id = 43211234;
|
||||
ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests(
|
||||
random_para_id.into()
|
||||
);
|
||||
Some((
|
||||
MultiAsset {
|
||||
fun: Fungible(EXISTENTIAL_DEPOSIT),
|
||||
id: Concrete(Parent.into())
|
||||
},
|
||||
ParentThen(Parachain(random_para_id).into()).into(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
use xcm::latest::prelude::*;
|
||||
use xcm_config::{DotLocation, MaxAssetsIntoHolding};
|
||||
use pallet_xcm_benchmarks::asset_instance_from;
|
||||
|
||||
@@ -476,11 +476,6 @@ pub type XcmRouter = WithUniqueTopic<(
|
||||
XcmpQueue,
|
||||
)>;
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
parameter_types! {
|
||||
pub ReachableDest: Option<MultiLocation> = Some(Parent.into());
|
||||
}
|
||||
|
||||
impl pallet_xcm::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
// We want to disallow users sending (arbitrary) XCMs from this chain.
|
||||
@@ -510,8 +505,6 @@ impl pallet_xcm::Config for Runtime {
|
||||
type SovereignAccountOf = LocationToAccountId;
|
||||
type MaxLockers = ConstU32<8>;
|
||||
type WeightInfo = crate::weights::pallet_xcm::WeightInfo<Runtime>;
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
type ReachableDest = ReachableDest;
|
||||
type AdminOrigin = EnsureRoot<AccountId>;
|
||||
type MaxRemoteLockConsumers = ConstU32<0>;
|
||||
type RemoteLockConsumerIdentifier = ();
|
||||
|
||||
@@ -19,12 +19,13 @@
|
||||
|
||||
use asset_hub_polkadot_runtime::xcm_config::{
|
||||
AssetFeeAsExistentialDepositMultiplierFeeCharger, CheckingAccount, DotLocation,
|
||||
ForeignCreatorsSovereignAccountOf, TrustBackedAssetsPalletLocation, XcmConfig,
|
||||
ForeignCreatorsSovereignAccountOf, LocationToAccountId, TrustBackedAssetsPalletLocation,
|
||||
XcmConfig,
|
||||
};
|
||||
pub use asset_hub_polkadot_runtime::{
|
||||
AllPalletsWithoutSystem, AssetDeposit, Assets, Balances, ExistentialDeposit, ForeignAssets,
|
||||
ForeignAssetsInstance, MetadataDepositBase, MetadataDepositPerByte, ParachainSystem, Runtime,
|
||||
RuntimeCall, RuntimeEvent, SessionKeys, System, TrustBackedAssetsInstance,
|
||||
RuntimeCall, RuntimeEvent, SessionKeys, System, TrustBackedAssetsInstance, XcmpQueue,
|
||||
};
|
||||
use asset_test_utils::{CollatorSessionKeys, ExtBuilder};
|
||||
use codec::{Decode, Encode};
|
||||
@@ -531,12 +532,6 @@ asset_test_utils::include_teleports_for_native_asset_works!(
|
||||
_ => None,
|
||||
}
|
||||
}),
|
||||
Box::new(|runtime_event_encoded: Vec<u8>| {
|
||||
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
|
||||
Ok(RuntimeEvent::XcmpQueue(event)) => Some(event),
|
||||
_ => None,
|
||||
}
|
||||
}),
|
||||
1000
|
||||
);
|
||||
|
||||
@@ -657,3 +652,32 @@ asset_test_utils::include_create_and_manage_foreign_assets_for_local_consensus_p
|
||||
assert_eq!(ForeignAssets::asset_ids().collect::<Vec<_>>().len(), 1);
|
||||
})
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn reserve_transfer_native_asset_to_non_teleport_para_works() {
|
||||
asset_test_utils::test_cases::reserve_transfer_native_asset_to_non_teleport_para_works::<
|
||||
Runtime,
|
||||
AllPalletsWithoutSystem,
|
||||
XcmConfig,
|
||||
ParachainSystem,
|
||||
XcmpQueue,
|
||||
LocationToAccountId,
|
||||
>(
|
||||
collator_session_keys(),
|
||||
ExistentialDeposit::get(),
|
||||
AccountId::from(ALICE),
|
||||
Box::new(|runtime_event_encoded: Vec<u8>| {
|
||||
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
|
||||
Ok(RuntimeEvent::PolkadotXcm(event)) => Some(event),
|
||||
_ => None,
|
||||
}
|
||||
}),
|
||||
Box::new(|runtime_event_encoded: Vec<u8>| {
|
||||
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
|
||||
Ok(RuntimeEvent::XcmpQueue(event)) => Some(event),
|
||||
_ => None,
|
||||
}
|
||||
}),
|
||||
WeightLimit::Unlimited,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1077,7 +1077,7 @@ mod benches {
|
||||
[pallet_xcm_bridge_hub_router, ToWestend]
|
||||
[pallet_xcm_bridge_hub_router, ToRococo]
|
||||
// XCM
|
||||
[pallet_xcm, PolkadotXcm]
|
||||
[pallet_xcm, PalletXcmExtrinsiscsBenchmark::<Runtime>]
|
||||
// NOTE: Make sure you point to the individual modules below.
|
||||
[pallet_xcm_benchmarks::fungible, XcmBalances]
|
||||
[pallet_xcm_benchmarks::generic, XcmGeneric]
|
||||
@@ -1315,6 +1315,7 @@ impl_runtime_apis! {
|
||||
use frame_support::traits::StorageInfoTrait;
|
||||
use frame_system_benchmarking::Pallet as SystemBench;
|
||||
use cumulus_pallet_session_benchmarking::Pallet as SessionBench;
|
||||
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark;
|
||||
use pallet_xcm_bridge_hub_router::benchmarking::Pallet as XcmBridgeHubRouterBench;
|
||||
|
||||
// This is defined once again in dispatch_benchmark, because list_benchmarks!
|
||||
@@ -1368,6 +1369,39 @@ impl_runtime_apis! {
|
||||
Config as XcmBridgeHubRouterConfig,
|
||||
};
|
||||
|
||||
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark;
|
||||
impl pallet_xcm::benchmarking::Config for Runtime {
|
||||
fn reachable_dest() -> Option<MultiLocation> {
|
||||
Some(Parent.into())
|
||||
}
|
||||
|
||||
fn teleportable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
|
||||
// Relay/native token can be teleported between AH and Relay.
|
||||
Some((
|
||||
MultiAsset {
|
||||
fun: Fungible(EXISTENTIAL_DEPOSIT),
|
||||
id: Concrete(Parent.into())
|
||||
},
|
||||
Parent.into(),
|
||||
))
|
||||
}
|
||||
|
||||
fn reserve_transferable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
|
||||
// AH can reserve transfer native token to some random parachain.
|
||||
let random_para_id = 43211234;
|
||||
ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests(
|
||||
random_para_id.into()
|
||||
);
|
||||
Some((
|
||||
MultiAsset {
|
||||
fun: Fungible(EXISTENTIAL_DEPOSIT),
|
||||
id: Concrete(Parent.into())
|
||||
},
|
||||
ParentThen(Parachain(random_para_id).into()).into(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl XcmBridgeHubRouterConfig<ToWococoXcmRouterInstance> for Runtime {
|
||||
fn make_congested() {
|
||||
cumulus_pallet_xcmp_queue::bridging::suspend_channel_for_benchmarks::<Runtime>(
|
||||
|
||||
@@ -667,11 +667,6 @@ pub type XcmRouter = WithUniqueTopic<(
|
||||
ToRococoXcmRouter,
|
||||
)>;
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
parameter_types! {
|
||||
pub ReachableDest: Option<MultiLocation> = Some(Parent.into());
|
||||
}
|
||||
|
||||
impl pallet_xcm::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
// We want to disallow users sending (arbitrary) XCMs from this chain.
|
||||
@@ -701,8 +696,6 @@ impl pallet_xcm::Config for Runtime {
|
||||
type SovereignAccountOf = LocationToAccountId;
|
||||
type MaxLockers = ConstU32<8>;
|
||||
type WeightInfo = crate::weights::pallet_xcm::WeightInfo<Runtime>;
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
type ReachableDest = ReachableDest;
|
||||
type AdminOrigin = EnsureRoot<AccountId>;
|
||||
type MaxRemoteLockConsumers = ConstU32<0>;
|
||||
type RemoteLockConsumerIdentifier = ();
|
||||
|
||||
@@ -529,12 +529,6 @@ asset_test_utils::include_teleports_for_native_asset_works!(
|
||||
_ => None,
|
||||
}
|
||||
}),
|
||||
Box::new(|runtime_event_encoded: Vec<u8>| {
|
||||
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
|
||||
Ok(RuntimeEvent::XcmpQueue(event)) => Some(event),
|
||||
_ => None,
|
||||
}
|
||||
}),
|
||||
1000
|
||||
);
|
||||
|
||||
@@ -930,6 +924,35 @@ mod asset_hub_rococo_tests {
|
||||
actual
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reserve_transfer_native_asset_to_non_teleport_para_works() {
|
||||
asset_test_utils::test_cases::reserve_transfer_native_asset_to_non_teleport_para_works::<
|
||||
Runtime,
|
||||
AllPalletsWithoutSystem,
|
||||
XcmConfig,
|
||||
ParachainSystem,
|
||||
XcmpQueue,
|
||||
LocationToAccountId,
|
||||
>(
|
||||
collator_session_keys(),
|
||||
ExistentialDeposit::get(),
|
||||
AccountId::from(ALICE),
|
||||
Box::new(|runtime_event_encoded: Vec<u8>| {
|
||||
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
|
||||
Ok(RuntimeEvent::PolkadotXcm(event)) => Some(event),
|
||||
_ => None,
|
||||
}
|
||||
}),
|
||||
Box::new(|runtime_event_encoded: Vec<u8>| {
|
||||
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
|
||||
Ok(RuntimeEvent::XcmpQueue(event)) => Some(event),
|
||||
_ => None,
|
||||
}
|
||||
}),
|
||||
WeightLimit::Unlimited,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mod asset_hub_wococo_tests {
|
||||
|
||||
@@ -1013,7 +1013,7 @@ mod benches {
|
||||
[cumulus_pallet_dmp_queue, DmpQueue]
|
||||
[pallet_xcm_bridge_hub_router, ToRococo]
|
||||
// XCM
|
||||
[pallet_xcm, PolkadotXcm]
|
||||
[pallet_xcm, PalletXcmExtrinsiscsBenchmark::<Runtime>]
|
||||
// NOTE: Make sure you point to the individual modules below.
|
||||
[pallet_xcm_benchmarks::fungible, XcmBalances]
|
||||
[pallet_xcm_benchmarks::generic, XcmGeneric]
|
||||
@@ -1297,6 +1297,7 @@ impl_runtime_apis! {
|
||||
use frame_support::traits::StorageInfoTrait;
|
||||
use frame_system_benchmarking::Pallet as SystemBench;
|
||||
use cumulus_pallet_session_benchmarking::Pallet as SessionBench;
|
||||
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark;
|
||||
use pallet_xcm_bridge_hub_router::benchmarking::Pallet as XcmBridgeHubRouterBench;
|
||||
|
||||
// This is defined once again in dispatch_benchmark, because list_benchmarks!
|
||||
@@ -1343,6 +1344,39 @@ impl_runtime_apis! {
|
||||
use cumulus_pallet_session_benchmarking::Pallet as SessionBench;
|
||||
impl cumulus_pallet_session_benchmarking::Config for Runtime {}
|
||||
|
||||
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsiscsBenchmark;
|
||||
impl pallet_xcm::benchmarking::Config for Runtime {
|
||||
fn reachable_dest() -> Option<MultiLocation> {
|
||||
Some(Parent.into())
|
||||
}
|
||||
|
||||
fn teleportable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
|
||||
// Relay/native token can be teleported between AH and Relay.
|
||||
Some((
|
||||
MultiAsset {
|
||||
fun: Fungible(EXISTENTIAL_DEPOSIT),
|
||||
id: Concrete(Parent.into())
|
||||
},
|
||||
Parent.into(),
|
||||
))
|
||||
}
|
||||
|
||||
fn reserve_transferable_asset_and_dest() -> Option<(MultiAsset, MultiLocation)> {
|
||||
// AH can reserve transfer native token to some random parachain.
|
||||
let random_para_id = 43211234;
|
||||
ParachainSystem::open_outbound_hrmp_channel_for_benchmarks_or_tests(
|
||||
random_para_id.into()
|
||||
);
|
||||
Some((
|
||||
MultiAsset {
|
||||
fun: Fungible(EXISTENTIAL_DEPOSIT),
|
||||
id: Concrete(Parent.into())
|
||||
},
|
||||
ParentThen(Parachain(random_para_id).into()).into(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
use pallet_xcm_bridge_hub_router::benchmarking::{
|
||||
Pallet as XcmBridgeHubRouterBench,
|
||||
Config as XcmBridgeHubRouterConfig,
|
||||
|
||||
@@ -636,11 +636,6 @@ pub type XcmRouter = WithUniqueTopic<(
|
||||
ToRococoXcmRouter,
|
||||
)>;
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
parameter_types! {
|
||||
pub ReachableDest: Option<MultiLocation> = Some(Parent.into());
|
||||
}
|
||||
|
||||
impl pallet_xcm::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type SendXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
|
||||
@@ -666,8 +661,6 @@ impl pallet_xcm::Config for Runtime {
|
||||
type SovereignAccountOf = LocationToAccountId;
|
||||
type MaxLockers = ConstU32<8>;
|
||||
type WeightInfo = crate::weights::pallet_xcm::WeightInfo<Runtime>;
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
type ReachableDest = ReachableDest;
|
||||
type AdminOrigin = EnsureRoot<AccountId>;
|
||||
type MaxRemoteLockConsumers = ConstU32<0>;
|
||||
type RemoteLockConsumerIdentifier = ();
|
||||
|
||||
@@ -525,12 +525,6 @@ asset_test_utils::include_teleports_for_native_asset_works!(
|
||||
_ => None,
|
||||
}
|
||||
}),
|
||||
Box::new(|runtime_event_encoded: Vec<u8>| {
|
||||
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
|
||||
Ok(RuntimeEvent::XcmpQueue(event)) => Some(event),
|
||||
_ => None,
|
||||
}
|
||||
}),
|
||||
1000
|
||||
);
|
||||
|
||||
@@ -815,3 +809,32 @@ fn change_xcm_bridge_hub_router_byte_fee_by_governance_works() {
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reserve_transfer_native_asset_to_non_teleport_para_works() {
|
||||
asset_test_utils::test_cases::reserve_transfer_native_asset_to_non_teleport_para_works::<
|
||||
Runtime,
|
||||
AllPalletsWithoutSystem,
|
||||
XcmConfig,
|
||||
ParachainSystem,
|
||||
XcmpQueue,
|
||||
LocationToAccountId,
|
||||
>(
|
||||
collator_session_keys(),
|
||||
ExistentialDeposit::get(),
|
||||
AccountId::from(ALICE),
|
||||
Box::new(|runtime_event_encoded: Vec<u8>| {
|
||||
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
|
||||
Ok(RuntimeEvent::PolkadotXcm(event)) => Some(event),
|
||||
_ => None,
|
||||
}
|
||||
}),
|
||||
Box::new(|runtime_event_encoded: Vec<u8>| {
|
||||
match RuntimeEvent::decode(&mut &runtime_event_encoded[..]) {
|
||||
Ok(RuntimeEvent::XcmpQueue(event)) => Some(event),
|
||||
_ => None,
|
||||
}
|
||||
}),
|
||||
WeightLimit::Unlimited,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,21 @@ use std::fmt::Debug;
|
||||
use xcm::latest::prelude::*;
|
||||
use xcm_builder::{CreateMatcher, MatchXcm};
|
||||
|
||||
/// Given a message, a sender, and a destination, it returns the delivery fees
|
||||
fn get_fungible_delivery_fees<S: SendXcm>(destination: MultiLocation, message: Xcm<()>) -> u128 {
|
||||
let Ok((_, delivery_fees)) = validate_send::<S>(destination, message) else {
|
||||
unreachable!("message can be sent; qed")
|
||||
};
|
||||
if let Some(delivery_fee) = delivery_fees.inner().first() {
|
||||
let Fungible(delivery_fee_amount) = delivery_fee.fun else {
|
||||
unreachable!("asset is fungible; qed");
|
||||
};
|
||||
delivery_fee_amount
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function to verify `xcm` contains all relevant instructions expected on destination
|
||||
/// chain as part of a reserve-asset-transfer.
|
||||
pub(crate) fn assert_matches_reserve_asset_deposited_instructions<RuntimeCall: Debug>(
|
||||
|
||||
@@ -16,25 +16,28 @@
|
||||
//! Module contains predefined test-case scenarios for `Runtime` with various assets.
|
||||
|
||||
use super::xcm_helpers;
|
||||
use crate::{assert_matches_reserve_asset_deposited_instructions, get_fungible_delivery_fees};
|
||||
use codec::Encode;
|
||||
use cumulus_primitives_core::XcmpMessageSource;
|
||||
use frame_support::{
|
||||
assert_noop, assert_ok,
|
||||
traits::{
|
||||
fungible::Mutate, fungibles::InspectEnumerable, Get, OnFinalize, OnInitialize, OriginTrait,
|
||||
fungible::Mutate, fungibles::InspectEnumerable, Currency, Get, OnFinalize, OnInitialize,
|
||||
OriginTrait,
|
||||
},
|
||||
weights::Weight,
|
||||
};
|
||||
use frame_system::pallet_prelude::BlockNumberFor;
|
||||
use parachains_common::{AccountId, Balance};
|
||||
use parachains_runtimes_test_utils::{
|
||||
assert_metadata, assert_total, AccountIdOf, BalanceOf, CollatorSessionKeys, ExtBuilder,
|
||||
ValidatorIdOf, XcmReceivedFrom,
|
||||
assert_metadata, assert_total, mock_open_hrmp_channel, AccountIdOf, BalanceOf,
|
||||
CollatorSessionKeys, ExtBuilder, ValidatorIdOf, XcmReceivedFrom,
|
||||
};
|
||||
use sp_runtime::{
|
||||
traits::{MaybeEquivalence, StaticLookup, Zero},
|
||||
DispatchError, Saturating,
|
||||
};
|
||||
use xcm::latest::prelude::*;
|
||||
use xcm::{latest::prelude::*, VersionedMultiAssets};
|
||||
use xcm_executor::{traits::ConvertLocation, XcmExecutor};
|
||||
|
||||
type RuntimeHelper<Runtime, AllPalletsWithoutSystem = ()> =
|
||||
@@ -43,8 +46,8 @@ type RuntimeHelper<Runtime, AllPalletsWithoutSystem = ()> =
|
||||
// Re-export test_case from `parachains-runtimes-test-utils`
|
||||
pub use parachains_runtimes_test_utils::test_cases::change_storage_constant_by_governance_works;
|
||||
|
||||
/// Test-case makes sure that `Runtime` can receive native asset from relay chain
|
||||
/// and can teleport it back and to the other parachains
|
||||
/// Test-case makes sure that `Runtime` can receive native asset from relay chain and can teleport
|
||||
/// it back
|
||||
pub fn teleports_for_native_asset_works<
|
||||
Runtime,
|
||||
AllPalletsWithoutSystem,
|
||||
@@ -57,9 +60,6 @@ pub fn teleports_for_native_asset_works<
|
||||
existential_deposit: BalanceOf<Runtime>,
|
||||
target_account: AccountIdOf<Runtime>,
|
||||
unwrap_pallet_xcm_event: Box<dyn Fn(Vec<u8>) -> Option<pallet_xcm::Event<Runtime>>>,
|
||||
unwrap_xcmp_queue_event: Box<
|
||||
dyn Fn(Vec<u8>) -> Option<cumulus_pallet_xcmp_queue::Event<Runtime>>,
|
||||
>,
|
||||
runtime_para_id: u32,
|
||||
) where
|
||||
Runtime: frame_system::Config
|
||||
@@ -164,12 +164,13 @@ pub fn teleports_for_native_asset_works<
|
||||
// 2. try to teleport asset back to the relaychain
|
||||
{
|
||||
let dest = MultiLocation::parent();
|
||||
let dest_beneficiary = MultiLocation::parent()
|
||||
let mut dest_beneficiary = MultiLocation::parent()
|
||||
.appended_with(AccountId32 {
|
||||
network: None,
|
||||
id: sp_runtime::AccountId32::new([3; 32]).into(),
|
||||
})
|
||||
.unwrap();
|
||||
dest_beneficiary.reanchor(&dest, XcmConfig::UniversalLocation::get()).unwrap();
|
||||
|
||||
let target_account_balance_before_teleport =
|
||||
<pallet_balances::Pallet<Runtime>>::free_balance(&target_account);
|
||||
@@ -223,65 +224,53 @@ pub fn teleports_for_native_asset_works<
|
||||
);
|
||||
}
|
||||
|
||||
// 3. try to teleport asset away to other parachain (1234)
|
||||
// 3. try to teleport assets away to other parachain (2345): should not work as we don't
|
||||
// trust `IsTeleporter` for `(relay-native-asset, para(2345))` pair
|
||||
{
|
||||
let other_para_id = 1234;
|
||||
let other_para_id = 2345;
|
||||
let dest = MultiLocation::new(1, X1(Parachain(other_para_id)));
|
||||
let dest_beneficiary = MultiLocation::new(1, X1(Parachain(other_para_id)))
|
||||
let mut dest_beneficiary = MultiLocation::new(1, X1(Parachain(other_para_id)))
|
||||
.appended_with(AccountId32 {
|
||||
network: None,
|
||||
id: sp_runtime::AccountId32::new([3; 32]).into(),
|
||||
})
|
||||
.unwrap();
|
||||
dest_beneficiary.reanchor(&dest, XcmConfig::UniversalLocation::get()).unwrap();
|
||||
|
||||
let target_account_balance_before_teleport =
|
||||
<pallet_balances::Pallet<Runtime>>::free_balance(&target_account);
|
||||
|
||||
let native_asset_to_teleport_away = native_asset_amount_unit * 3.into();
|
||||
assert!(
|
||||
native_asset_to_teleport_away <
|
||||
target_account_balance_before_teleport - existential_deposit
|
||||
);
|
||||
|
||||
assert_ok!(RuntimeHelper::<Runtime>::do_teleport_assets::<HrmpChannelOpener>(
|
||||
RuntimeHelper::<Runtime>::origin_of(target_account.clone()),
|
||||
dest,
|
||||
dest_beneficiary,
|
||||
(native_asset_id, native_asset_to_teleport_away.into()),
|
||||
Some((runtime_para_id, other_para_id)),
|
||||
included_head,
|
||||
&alice,
|
||||
));
|
||||
|
||||
let delivery_fees =
|
||||
xcm_helpers::transfer_assets_delivery_fees::<XcmConfig::XcmSender>(
|
||||
(native_asset_id, native_asset_to_teleport_away.into()).into(),
|
||||
0,
|
||||
Unlimited,
|
||||
dest_beneficiary,
|
||||
assert_eq!(
|
||||
RuntimeHelper::<Runtime>::do_teleport_assets::<HrmpChannelOpener>(
|
||||
RuntimeHelper::<Runtime>::origin_of(target_account.clone()),
|
||||
dest,
|
||||
);
|
||||
dest_beneficiary,
|
||||
(native_asset_id, native_asset_to_teleport_away.into()),
|
||||
Some((runtime_para_id, other_para_id)),
|
||||
included_head,
|
||||
&alice,
|
||||
),
|
||||
Err(DispatchError::Module(sp_runtime::ModuleError {
|
||||
index: 31,
|
||||
error: [2, 0, 0, 0,],
|
||||
message: Some("Filtered",),
|
||||
},),)
|
||||
);
|
||||
|
||||
// check balances
|
||||
assert_eq!(
|
||||
<pallet_balances::Pallet<Runtime>>::free_balance(&target_account),
|
||||
target_account_balance_before_teleport -
|
||||
native_asset_to_teleport_away -
|
||||
delivery_fees.into()
|
||||
target_account_balance_before_teleport
|
||||
);
|
||||
assert_eq!(
|
||||
<pallet_balances::Pallet<Runtime>>::free_balance(&CheckingAccount::get()),
|
||||
0.into()
|
||||
);
|
||||
|
||||
// check events
|
||||
RuntimeHelper::<Runtime>::assert_pallet_xcm_event_outcome(
|
||||
&unwrap_pallet_xcm_event,
|
||||
|outcome| {
|
||||
assert_ok!(outcome.ensure_complete());
|
||||
},
|
||||
);
|
||||
assert!(RuntimeHelper::<Runtime>::xcmp_queue_message_sent(unwrap_xcmp_queue_event)
|
||||
.is_some());
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -298,7 +287,6 @@ macro_rules! include_teleports_for_native_asset_works(
|
||||
$collator_session_key:expr,
|
||||
$existential_deposit:expr,
|
||||
$unwrap_pallet_xcm_event:expr,
|
||||
$unwrap_xcmp_queue_event:expr,
|
||||
$runtime_para_id:expr
|
||||
) => {
|
||||
#[test]
|
||||
@@ -318,15 +306,14 @@ macro_rules! include_teleports_for_native_asset_works(
|
||||
$existential_deposit,
|
||||
target_account,
|
||||
$unwrap_pallet_xcm_event,
|
||||
$unwrap_xcmp_queue_event,
|
||||
$runtime_para_id
|
||||
)
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/// Test-case makes sure that `Runtime` can receive teleported assets from sibling parachain relay
|
||||
/// chain
|
||||
/// Test-case makes sure that `Runtime` can receive teleported assets from sibling parachain, and
|
||||
/// can teleport it back
|
||||
pub fn teleports_for_foreign_assets_works<
|
||||
Runtime,
|
||||
AllPalletsWithoutSystem,
|
||||
@@ -381,7 +368,7 @@ pub fn teleports_for_foreign_assets_works<
|
||||
<Runtime as frame_system::Config>::AccountId: From<AccountId>,
|
||||
ForeignAssetsPalletInstance: 'static,
|
||||
{
|
||||
// foreign parachain with the same consenus currency as asset
|
||||
// foreign parachain with the same consensus currency as asset
|
||||
let foreign_para_id = 2222;
|
||||
let foreign_asset_id_multilocation = MultiLocation {
|
||||
parents: 1,
|
||||
@@ -473,7 +460,7 @@ pub fn teleports_for_foreign_assets_works<
|
||||
>(foreign_asset_id_multilocation, 0, 0);
|
||||
assert!(teleported_foreign_asset_amount > asset_minimum_asset_balance);
|
||||
|
||||
// 1. process received teleported assets from relaychain
|
||||
// 1. process received teleported assets from sibling parachain (foreign_para_id)
|
||||
let xcm = Xcm(vec![
|
||||
// BuyExecution with relaychain native token
|
||||
WithdrawAsset(buy_execution_fee.clone().into()),
|
||||
@@ -551,12 +538,13 @@ pub fn teleports_for_foreign_assets_works<
|
||||
// 2. try to teleport asset back to source parachain (foreign_para_id)
|
||||
{
|
||||
let dest = MultiLocation::new(1, X1(Parachain(foreign_para_id)));
|
||||
let dest_beneficiary = MultiLocation::new(1, X1(Parachain(foreign_para_id)))
|
||||
let mut dest_beneficiary = MultiLocation::new(1, X1(Parachain(foreign_para_id)))
|
||||
.appended_with(AccountId32 {
|
||||
network: None,
|
||||
id: sp_runtime::AccountId32::new([3; 32]).into(),
|
||||
})
|
||||
.unwrap();
|
||||
dest_beneficiary.reanchor(&dest, XcmConfig::UniversalLocation::get()).unwrap();
|
||||
|
||||
let target_account_balance_before_teleport =
|
||||
<pallet_assets::Pallet<Runtime, ForeignAssetsPalletInstance>>::balance(
|
||||
@@ -1108,7 +1096,7 @@ pub fn create_and_manage_foreign_assets_for_local_consensus_parachain_assets_wor
|
||||
AssetId: Clone + Copy,
|
||||
AssetIdConverter: MaybeEquivalence<MultiLocation, AssetId>,
|
||||
{
|
||||
// foreign parachain with the same consenus currency as asset
|
||||
// foreign parachain with the same consensus currency as asset
|
||||
let foreign_asset_id_multilocation =
|
||||
MultiLocation { parents: 1, interior: X2(Parachain(2222), GeneralIndex(1234567)) };
|
||||
let asset_id = AssetIdConverter::convert(&foreign_asset_id_multilocation).unwrap();
|
||||
@@ -1388,3 +1376,199 @@ macro_rules! include_create_and_manage_foreign_assets_for_local_consensus_parach
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/// Test-case makes sure that `Runtime` can reserve-transfer asset to other parachains (where
|
||||
/// teleport is not trusted)
|
||||
pub fn reserve_transfer_native_asset_to_non_teleport_para_works<
|
||||
Runtime,
|
||||
AllPalletsWithoutSystem,
|
||||
XcmConfig,
|
||||
HrmpChannelOpener,
|
||||
HrmpChannelSource,
|
||||
LocationToAccountId,
|
||||
>(
|
||||
collator_session_keys: CollatorSessionKeys<Runtime>,
|
||||
existential_deposit: BalanceOf<Runtime>,
|
||||
alice_account: AccountIdOf<Runtime>,
|
||||
unwrap_pallet_xcm_event: Box<dyn Fn(Vec<u8>) -> Option<pallet_xcm::Event<Runtime>>>,
|
||||
unwrap_xcmp_queue_event: Box<
|
||||
dyn Fn(Vec<u8>) -> Option<cumulus_pallet_xcmp_queue::Event<Runtime>>,
|
||||
>,
|
||||
weight_limit: WeightLimit,
|
||||
) where
|
||||
Runtime: frame_system::Config
|
||||
+ pallet_balances::Config
|
||||
+ pallet_session::Config
|
||||
+ pallet_xcm::Config
|
||||
+ parachain_info::Config
|
||||
+ pallet_collator_selection::Config
|
||||
+ cumulus_pallet_parachain_system::Config
|
||||
+ cumulus_pallet_xcmp_queue::Config,
|
||||
AllPalletsWithoutSystem:
|
||||
OnInitialize<BlockNumberFor<Runtime>> + OnFinalize<BlockNumberFor<Runtime>>,
|
||||
AccountIdOf<Runtime>: Into<[u8; 32]>,
|
||||
ValidatorIdOf<Runtime>: From<AccountIdOf<Runtime>>,
|
||||
BalanceOf<Runtime>: From<Balance>,
|
||||
<Runtime as pallet_balances::Config>::Balance: From<Balance> + Into<u128>,
|
||||
XcmConfig: xcm_executor::Config,
|
||||
LocationToAccountId: ConvertLocation<AccountIdOf<Runtime>>,
|
||||
<Runtime as frame_system::Config>::AccountId:
|
||||
Into<<<Runtime as frame_system::Config>::RuntimeOrigin as OriginTrait>::AccountId>,
|
||||
<<Runtime as frame_system::Config>::Lookup as StaticLookup>::Source:
|
||||
From<<Runtime as frame_system::Config>::AccountId>,
|
||||
<Runtime as frame_system::Config>::AccountId: From<AccountId>,
|
||||
HrmpChannelOpener: frame_support::inherent::ProvideInherent<
|
||||
Call = cumulus_pallet_parachain_system::Call<Runtime>,
|
||||
>,
|
||||
HrmpChannelSource: XcmpMessageSource,
|
||||
{
|
||||
let runtime_para_id = 1000;
|
||||
ExtBuilder::<Runtime>::default()
|
||||
.with_collators(collator_session_keys.collators())
|
||||
.with_session_keys(collator_session_keys.session_keys())
|
||||
.with_tracing()
|
||||
.with_safe_xcm_version(3)
|
||||
.with_para_id(runtime_para_id.into())
|
||||
.build()
|
||||
.execute_with(|| {
|
||||
let mut alice = [0u8; 32];
|
||||
alice[0] = 1;
|
||||
let included_head = RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::run_to_block(
|
||||
2,
|
||||
AccountId::from(alice).into(),
|
||||
);
|
||||
|
||||
// reserve-transfer native asset with local reserve to remote parachain (2345)
|
||||
|
||||
let other_para_id = 2345;
|
||||
let native_asset = MultiLocation::parent();
|
||||
let dest = MultiLocation::new(1, X1(Parachain(other_para_id)));
|
||||
let mut dest_beneficiary = MultiLocation::new(1, X1(Parachain(other_para_id)))
|
||||
.appended_with(AccountId32 {
|
||||
network: None,
|
||||
id: sp_runtime::AccountId32::new([3; 32]).into(),
|
||||
})
|
||||
.unwrap();
|
||||
dest_beneficiary.reanchor(&dest, XcmConfig::UniversalLocation::get()).unwrap();
|
||||
|
||||
let reserve_account = LocationToAccountId::convert_location(&dest)
|
||||
.expect("Sovereign account for reserves");
|
||||
let balance_to_transfer = 1_000_000_000_000_u128;
|
||||
|
||||
// open HRMP to other parachain
|
||||
mock_open_hrmp_channel::<Runtime, HrmpChannelOpener>(
|
||||
runtime_para_id.into(),
|
||||
other_para_id.into(),
|
||||
included_head,
|
||||
&alice,
|
||||
);
|
||||
|
||||
// we calculate exact delivery fees _after_ sending the message by weighing the sent
|
||||
// xcm, and this delivery fee varies for different runtimes, so just add enough buffer,
|
||||
// then verify the arithmetics check out on final balance.
|
||||
let delivery_fees_buffer = 40_000_000_000u128;
|
||||
// drip 2xED + transfer_amount + delivery_fees_buffer to Alice account
|
||||
let alice_account_init_balance = existential_deposit.saturating_mul(2.into()) +
|
||||
balance_to_transfer.into() +
|
||||
delivery_fees_buffer.into();
|
||||
let _ = <pallet_balances::Pallet<Runtime>>::deposit_creating(
|
||||
&alice_account,
|
||||
alice_account_init_balance,
|
||||
);
|
||||
// SA of target location needs to have at least ED, otherwise making reserve fails
|
||||
let _ = <pallet_balances::Pallet<Runtime>>::deposit_creating(
|
||||
&reserve_account,
|
||||
existential_deposit,
|
||||
);
|
||||
|
||||
// we just check here, that user retains enough balance after withdrawal
|
||||
// and also we check if `balance_to_transfer` is more than `existential_deposit`,
|
||||
assert!(
|
||||
(<pallet_balances::Pallet<Runtime>>::free_balance(&alice_account) -
|
||||
balance_to_transfer.into()) >=
|
||||
existential_deposit
|
||||
);
|
||||
// SA has just ED
|
||||
assert_eq!(
|
||||
<pallet_balances::Pallet<Runtime>>::free_balance(&reserve_account),
|
||||
existential_deposit
|
||||
);
|
||||
|
||||
// local native asset (pallet_balances)
|
||||
let asset_to_transfer = MultiAsset {
|
||||
fun: Fungible(balance_to_transfer.into()),
|
||||
id: Concrete(native_asset),
|
||||
};
|
||||
|
||||
// pallet_xcm call reserve transfer
|
||||
assert_ok!(<pallet_xcm::Pallet<Runtime>>::limited_reserve_transfer_assets(
|
||||
RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::origin_of(alice_account.clone()),
|
||||
Box::new(dest.into_versioned()),
|
||||
Box::new(dest_beneficiary.into_versioned()),
|
||||
Box::new(VersionedMultiAssets::from(MultiAssets::from(asset_to_transfer))),
|
||||
0,
|
||||
weight_limit,
|
||||
));
|
||||
|
||||
// check events
|
||||
// check pallet_xcm attempted
|
||||
RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::assert_pallet_xcm_event_outcome(
|
||||
&unwrap_pallet_xcm_event,
|
||||
|outcome| {
|
||||
assert_ok!(outcome.ensure_complete());
|
||||
},
|
||||
);
|
||||
|
||||
// check that xcm was sent
|
||||
let xcm_sent_message_hash = <frame_system::Pallet<Runtime>>::events()
|
||||
.into_iter()
|
||||
.filter_map(|e| unwrap_xcmp_queue_event(e.event.encode()))
|
||||
.find_map(|e| match e {
|
||||
cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { message_hash } =>
|
||||
Some(message_hash),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
// read xcm
|
||||
let xcm_sent = RuntimeHelper::<HrmpChannelSource, AllPalletsWithoutSystem>::take_xcm(
|
||||
other_para_id.into(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let delivery_fees = get_fungible_delivery_fees::<
|
||||
<XcmConfig as xcm_executor::Config>::XcmSender,
|
||||
>(dest, Xcm::try_from(xcm_sent.clone()).unwrap());
|
||||
|
||||
assert_eq!(
|
||||
xcm_sent_message_hash,
|
||||
Some(xcm_sent.using_encoded(sp_io::hashing::blake2_256))
|
||||
);
|
||||
let mut xcm_sent: Xcm<()> = xcm_sent.try_into().expect("versioned xcm");
|
||||
|
||||
// check sent XCM Program to other parachain
|
||||
println!("reserve_transfer_native_asset_works sent xcm: {:?}", xcm_sent);
|
||||
let reserve_assets_deposited = MultiAssets::from(vec![MultiAsset {
|
||||
id: Concrete(MultiLocation { parents: 1, interior: Here }),
|
||||
fun: Fungible(1000000000000),
|
||||
}]);
|
||||
|
||||
assert_matches_reserve_asset_deposited_instructions(
|
||||
&mut xcm_sent,
|
||||
&reserve_assets_deposited,
|
||||
&dest_beneficiary,
|
||||
);
|
||||
|
||||
// check alice account decreased by balance_to_transfer ( + delivery_fees)
|
||||
assert_eq!(
|
||||
<pallet_balances::Pallet<Runtime>>::free_balance(&alice_account),
|
||||
alice_account_init_balance - balance_to_transfer.into() - delivery_fees.into()
|
||||
);
|
||||
|
||||
// check reserve account
|
||||
// check reserve account increased by balance_to_transfer
|
||||
assert_eq!(
|
||||
<pallet_balances::Pallet<Runtime>>::free_balance(&reserve_account),
|
||||
existential_deposit + balance_to_transfer.into()
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
//! Module contains predefined test-case scenarios for `Runtime` with various assets transferred
|
||||
//! over a bridge.
|
||||
|
||||
use crate::assert_matches_reserve_asset_deposited_instructions;
|
||||
use crate::{assert_matches_reserve_asset_deposited_instructions, get_fungible_delivery_fees};
|
||||
use codec::Encode;
|
||||
use cumulus_primitives_core::XcmpMessageSource;
|
||||
use frame_support::{
|
||||
@@ -32,10 +32,7 @@ use parachains_runtimes_test_utils::{
|
||||
use sp_runtime::{traits::StaticLookup, Saturating};
|
||||
use xcm::{latest::prelude::*, VersionedMultiAssets};
|
||||
use xcm_builder::{CreateMatcher, MatchXcm};
|
||||
use xcm_executor::{
|
||||
traits::{ConvertLocation, TransactAsset},
|
||||
XcmExecutor,
|
||||
};
|
||||
use xcm_executor::{traits::ConvertLocation, XcmExecutor};
|
||||
|
||||
pub struct TestBridgingConfig {
|
||||
pub bridged_network: NetworkId,
|
||||
@@ -129,8 +126,13 @@ pub fn limited_reserve_transfer_assets_for_native_asset_works<
|
||||
&alice,
|
||||
);
|
||||
|
||||
// drip ED to account
|
||||
let alice_account_init_balance = existential_deposit + balance_to_transfer.into();
|
||||
// we calculate exact delivery fees _after_ sending the message by weighing the sent
|
||||
// xcm, and this delivery fee varies for different runtimes, so just add enough buffer,
|
||||
// then verify the arithmetics check out on final balance.
|
||||
let delivery_fees_buffer = 8_000_000_000_000u128;
|
||||
// drip ED + transfer_amount + delivery_fees_buffer to Alice account
|
||||
let alice_account_init_balance =
|
||||
existential_deposit + balance_to_transfer.into() + delivery_fees_buffer.into();
|
||||
let _ = <pallet_balances::Pallet<Runtime>>::deposit_creating(
|
||||
&alice_account,
|
||||
alice_account_init_balance,
|
||||
@@ -183,56 +185,6 @@ pub fn limited_reserve_transfer_assets_for_native_asset_works<
|
||||
|
||||
let expected_beneficiary = target_destination_account;
|
||||
|
||||
// Make sure sender has enough funds for paying delivery fees
|
||||
let handling_delivery_fees = {
|
||||
// Probable XCM with `ReserveAssetDeposited`.
|
||||
let mut expected_reserve_asset_deposited_message = Xcm(vec![
|
||||
ReserveAssetDeposited(MultiAssets::from(expected_assets.clone())),
|
||||
ClearOrigin,
|
||||
BuyExecution {
|
||||
fees: MultiAsset {
|
||||
id: Concrete(Default::default()),
|
||||
fun: Fungible(balance_to_transfer),
|
||||
},
|
||||
weight_limit: Unlimited,
|
||||
},
|
||||
DepositAsset { assets: Wild(AllCounted(1)), beneficiary: expected_beneficiary },
|
||||
SetTopic([
|
||||
220, 188, 144, 32, 213, 83, 111, 175, 44, 210, 111, 19, 90, 165, 191, 112,
|
||||
140, 247, 192, 124, 42, 17, 153, 141, 114, 34, 189, 20, 83, 69, 237, 173,
|
||||
]),
|
||||
]);
|
||||
assert_matches_reserve_asset_deposited_instructions(
|
||||
&mut expected_reserve_asset_deposited_message,
|
||||
&expected_assets,
|
||||
&expected_beneficiary,
|
||||
);
|
||||
|
||||
// Call `SendXcm::validate` to get delivery fees.
|
||||
let (_, delivery_fees): (_, MultiAssets) = XcmConfig::XcmSender::validate(
|
||||
&mut Some(target_location_from_different_consensus),
|
||||
&mut Some(expected_reserve_asset_deposited_message),
|
||||
)
|
||||
.expect("validate passes");
|
||||
// Drip delivery fee to Alice account.
|
||||
let mut delivery_fees_added = false;
|
||||
for delivery_fee in delivery_fees.inner() {
|
||||
assert_ok!(<XcmConfig::AssetTransactor as TransactAsset>::deposit_asset(
|
||||
&delivery_fee,
|
||||
&MultiLocation {
|
||||
parents: 0,
|
||||
interior: X1(AccountId32 {
|
||||
network: None,
|
||||
id: alice_account.clone().into(),
|
||||
}),
|
||||
},
|
||||
None,
|
||||
));
|
||||
delivery_fees_added = true;
|
||||
}
|
||||
delivery_fees_added
|
||||
};
|
||||
|
||||
// do pallet_xcm call reserve transfer
|
||||
assert_ok!(<pallet_xcm::Pallet<Runtime>>::limited_reserve_transfer_assets(
|
||||
RuntimeHelper::<Runtime, AllPalletsWithoutSystem>::origin_of(alice_account.clone()),
|
||||
@@ -275,6 +227,7 @@ pub fn limited_reserve_transfer_assets_for_native_asset_works<
|
||||
|
||||
// check sent XCM ExportMessage to BridgeHub
|
||||
|
||||
let mut delivery_fees = 0;
|
||||
// 1. check paid or unpaid
|
||||
if let Some(expected_fee_asset_id) = maybe_paid_export_message {
|
||||
xcm_sent
|
||||
@@ -315,6 +268,10 @@ pub fn limited_reserve_transfer_assets_for_native_asset_works<
|
||||
.split_global()
|
||||
.expect("split works");
|
||||
assert_eq!(destination, &target_location_junctions_without_global_consensus);
|
||||
// Call `SendXcm::validate` to get delivery fees.
|
||||
delivery_fees = get_fungible_delivery_fees::<
|
||||
<XcmConfig as xcm_executor::Config>::XcmSender,
|
||||
>(target_location_from_different_consensus, inner_xcm.clone());
|
||||
assert_matches_reserve_asset_deposited_instructions(
|
||||
inner_xcm,
|
||||
&expected_assets,
|
||||
@@ -330,8 +287,8 @@ pub fn limited_reserve_transfer_assets_for_native_asset_works<
|
||||
assert_eq!(
|
||||
<pallet_balances::Pallet<Runtime>>::free_balance(&alice_account),
|
||||
alice_account_init_balance
|
||||
.saturating_sub(existential_deposit)
|
||||
.saturating_sub(balance_to_transfer.into())
|
||||
.saturating_sub(delivery_fees.into())
|
||||
);
|
||||
|
||||
// check reserve account increased by balance_to_transfer
|
||||
@@ -341,14 +298,13 @@ pub fn limited_reserve_transfer_assets_for_native_asset_works<
|
||||
);
|
||||
|
||||
// check dedicated account increased by delivery fees (if configured)
|
||||
if handling_delivery_fees {
|
||||
if let Some(delivery_fees_account) = delivery_fees_account {
|
||||
let delivery_fees_account_balance_after =
|
||||
<pallet_balances::Pallet<Runtime>>::free_balance(&delivery_fees_account);
|
||||
assert!(
|
||||
delivery_fees_account_balance_after > delivery_fees_account_balance_before
|
||||
);
|
||||
}
|
||||
if let Some(delivery_fees_account) = delivery_fees_account {
|
||||
let delivery_fees_account_balance_after =
|
||||
<pallet_balances::Pallet<Runtime>>::free_balance(&delivery_fees_account);
|
||||
assert!(
|
||||
delivery_fees_account_balance_after - delivery_fees.into() >=
|
||||
delivery_fees_account_balance_before
|
||||
);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user