diff --git a/polkadot-parachains/parachains-common/src/impls.rs b/polkadot-parachains/parachains-common/src/impls.rs index addbc788b12..35dd740f521 100644 --- a/polkadot-parachains/parachains-common/src/impls.rs +++ b/polkadot-parachains/parachains-common/src/impls.rs @@ -22,9 +22,10 @@ use frame_support::traits::{ }; use pallet_asset_tx_payment::HandleCredit; use sp_runtime::traits::Zero; -use sp_std::marker::PhantomData; -use xcm::latest::{AssetId, Fungibility::Fungible, MultiAsset, MultiLocation}; -use xcm_executor::traits::FilterAssetLocation; +use sp_std::{borrow::Borrow, marker::PhantomData, result}; +use xcm::latest::{AssetId, Fungibility::Fungible, Junction, MultiAsset, MultiLocation}; + +use xcm_executor::traits::{Convert, FilterAssetLocation}; /// Type alias to conveniently refer to the `Currency::NegativeImbalance` associated type. pub type NegativeImbalance = as Currency< @@ -113,6 +114,44 @@ impl> FilterAssetLocation for AssetsFrom { } } +/// Converter struct with two main usage scenarios: +/// 1. Transfer asset from local to other para-chain +/// - with `reverse_ref` to convert a numeric asset ID (must be `TryFrom/TryInto`) into a `GeneralIndex` junction +/// 2. Transfer asset back from other para-chain to local +/// - with `convert_ref` to convert multilocation struct `(1,X2(ParaChain(para_id),GeneralIndex(general_index))` to a numeric asset ID +pub struct AsPrefixedGeneralIndexFromLocalOrRemote< + LocalPrefix, + RemotePrefix, + AssetId, + ConvertAssetId, +>(PhantomData<(LocalPrefix, RemotePrefix, AssetId, ConvertAssetId)>); +impl< + LocalPrefix: Get, + RemotePrefix: Get, + AssetId: Clone, + ConvertAssetId: Convert, + > Convert + for AsPrefixedGeneralIndexFromLocalOrRemote +{ + fn convert_ref(id: impl Borrow) -> result::Result { + let id = id.borrow(); + + match id.match_and_split(&LocalPrefix::get()) { + Some(Junction::GeneralIndex(id)) => ConvertAssetId::convert_ref(id), + _ => match id.match_and_split(&RemotePrefix::get()) { + Some(Junction::GeneralIndex(id)) => ConvertAssetId::convert_ref(id), + _ => Err(()), + }, + } + } + fn reverse_ref(what: impl Borrow) -> result::Result { + let mut location = LocalPrefix::get(); + let id = ConvertAssetId::reverse_ref(what)?; + location.push_interior(Junction::GeneralIndex(id)).map_err(|_| ())?; + Ok(location) + } +} + #[cfg(test)] mod tests { use super::*; @@ -130,7 +169,9 @@ mod tests { traits::{BlakeTwo256, IdentityLookup}, Perbill, }; + use xcm::prelude::*; + use xcm_executor::traits::JustTry; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -281,4 +322,59 @@ mod tests { "AssetsFrom should allow assets from any of its interior locations" ); } + + #[test] + fn assets_from_convert_correctly() { + parameter_types! { + pub const Local: MultiLocation = Here.into(); + pub Remote: MultiLocation = MultiLocation::new(1, X1(Parachain(1000))); + } + let local_asset_location = Local::get().pushed_with_interior(GeneralIndex(42)).unwrap(); + let remote_asset_location = Remote::get().pushed_with_interior(GeneralIndex(42)).unwrap(); + let mut asset_id = + AsPrefixedGeneralIndexFromLocalOrRemote::::convert_ref( + &local_asset_location, + ) + .unwrap_or(u32::default()); + assert_eq!(asset_id, 42); + asset_id = + AsPrefixedGeneralIndexFromLocalOrRemote::::convert_ref( + &remote_asset_location, + ) + .unwrap_or(u32::default()); + assert_eq!(asset_id, 42); + } + + #[test] + fn assets_from_convert_error_with_different_parent() { + parameter_types! { + pub const Local: MultiLocation = Here.into(); + pub Remote: MultiLocation = MultiLocation::new(2, Junctions::Here); + } + let remote_asset_location = MultiLocation::new(1, X1(GeneralIndex(42))); + let asset_id = + AsPrefixedGeneralIndexFromLocalOrRemote::::convert_ref( + &remote_asset_location, + ) + .unwrap_or(u32::default()); + assert_eq!(asset_id, u32::default()) + } + + #[test] + fn assets_from_convert_error_with_different_interiors() { + parameter_types! { + pub const Local: MultiLocation = Here.into(); + pub Remote: MultiLocation = MultiLocation::new(1, X1(Parachain(1000))); + } + let mut remote_asset_location = + Remote::get().pushed_with_interior(PalletInstance(10)).unwrap(); + remote_asset_location = + remote_asset_location.pushed_with_interior(GeneralIndex(42)).unwrap(); + let asset_id = + AsPrefixedGeneralIndexFromLocalOrRemote::::convert_ref( + &remote_asset_location, + ) + .unwrap_or(u32::default()); + assert_eq!(asset_id, u32::default()) + } } diff --git a/polkadot-parachains/statemine/src/lib.rs b/polkadot-parachains/statemine/src/lib.rs index 83934708aab..3f93bbc8d05 100644 --- a/polkadot-parachains/statemine/src/lib.rs +++ b/polkadot-parachains/statemine/src/lib.rs @@ -65,17 +65,18 @@ use parachains_common::{ pub use sp_runtime::BuildStorage; // Polkadot imports +use crate::common::impls::AsPrefixedGeneralIndexFromLocalOrRemote; use pallet_xcm::{EnsureXcm, IsMajorityOfBody, XcmPassthrough}; use polkadot_parachain::primitives::Sibling; use polkadot_runtime_common::{BlockHashCount, RocksDbWeight, SlowAdjustingFeeUpdate}; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, - AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, - ConvertedConcreteAssetId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, - FungiblesAdapter, IsConcrete, LocationInverter, NativeAsset, ParentAsSuperuser, - ParentIsDefault, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, + AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, ConvertedConcreteAssetId, + CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, IsConcrete, + LocationInverter, NativeAsset, ParentAsSuperuser, ParentIsDefault, RelayChainAsNative, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, }; use xcm_executor::{traits::JustTry, Config, XcmExecutor}; @@ -433,6 +434,7 @@ parameter_types! { pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); pub const Local: MultiLocation = Here.into(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); + pub Remote: MultiLocation = MultiLocation::new(1, X1(Parachain(ParachainInfo::parachain_id().into()))); } /// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used @@ -469,7 +471,7 @@ pub type FungiblesTransactor = FungiblesAdapter< ConvertedConcreteAssetId< AssetId, Balance, - AsPrefixedGeneralIndex, + AsPrefixedGeneralIndexFromLocalOrRemote, JustTry, >, // Convert an XCM MultiLocation into a local account id: diff --git a/polkadot-parachains/statemint/src/lib.rs b/polkadot-parachains/statemint/src/lib.rs index f625880d3bb..3201d22e1cb 100644 --- a/polkadot-parachains/statemint/src/lib.rs +++ b/polkadot-parachains/statemint/src/lib.rs @@ -65,17 +65,18 @@ use parachains_common::{ pub use sp_runtime::BuildStorage; // Polkadot imports +use crate::common::impls::AsPrefixedGeneralIndexFromLocalOrRemote; use pallet_xcm::{EnsureXcm, IsMajorityOfBody, XcmPassthrough}; use polkadot_parachain::primitives::Sibling; use polkadot_runtime_common::{BlockHashCount, RocksDbWeight, SlowAdjustingFeeUpdate}; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, - AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, - ConvertedConcreteAssetId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, - FungiblesAdapter, IsConcrete, LocationInverter, NativeAsset, ParentAsSuperuser, - ParentIsDefault, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, + AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, ConvertedConcreteAssetId, + CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, IsConcrete, + LocationInverter, NativeAsset, ParentAsSuperuser, ParentIsDefault, RelayChainAsNative, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, }; use xcm_executor::{traits::JustTry, Config, XcmExecutor}; @@ -445,6 +446,7 @@ parameter_types! { pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); pub const Local: MultiLocation = Here.into(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); + pub Remote: MultiLocation = MultiLocation::new(1, X1(Parachain(ParachainInfo::parachain_id().into()))); } /// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used @@ -481,7 +483,7 @@ pub type FungiblesTransactor = FungiblesAdapter< ConvertedConcreteAssetId< AssetId, Balance, - AsPrefixedGeneralIndex, + AsPrefixedGeneralIndexFromLocalOrRemote, JustTry, >, // Convert an XCM MultiLocation into a local account id: diff --git a/polkadot-parachains/westmint/src/lib.rs b/polkadot-parachains/westmint/src/lib.rs index 22a37c5435e..de3d2cc62c9 100644 --- a/polkadot-parachains/westmint/src/lib.rs +++ b/polkadot-parachains/westmint/src/lib.rs @@ -65,18 +65,18 @@ use parachains_common::{ pub use sp_runtime::BuildStorage; // Polkadot imports +use crate::common::impls::AsPrefixedGeneralIndexFromLocalOrRemote; use pallet_xcm::XcmPassthrough; use polkadot_parachain::primitives::Sibling; use polkadot_runtime_common::{BlockHashCount, RocksDbWeight, SlowAdjustingFeeUpdate}; use xcm::latest::prelude::*; use xcm_builder::{ AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, - AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, - ConvertedConcreteAssetId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, - FungiblesAdapter, IsConcrete, LocationInverter, NativeAsset, ParentAsSuperuser, - ParentIsDefault, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, - UsingComponents, + AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, ConvertedConcreteAssetId, + CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, IsConcrete, + LocationInverter, NativeAsset, ParentAsSuperuser, ParentIsDefault, RelayChainAsNative, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, }; use xcm_executor::{traits::JustTry, Config, XcmExecutor}; @@ -428,6 +428,7 @@ parameter_types! { pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); pub const Local: MultiLocation = Here.into(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); + pub Remote: MultiLocation = MultiLocation::new(1, X1(Parachain(ParachainInfo::parachain_id().into()))); } /// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used @@ -464,7 +465,7 @@ pub type FungiblesTransactor = FungiblesAdapter< ConvertedConcreteAssetId< AssetId, Balance, - AsPrefixedGeneralIndex, + AsPrefixedGeneralIndexFromLocalOrRemote, JustTry, >, // Convert an XCM MultiLocation into a local account id: @@ -477,6 +478,7 @@ pub type FungiblesTransactor = FungiblesAdapter< // The account to use for tracking teleports. CheckingAccount, >; + /// Means for transacting assets on this chain. pub type AssetTransactors = (CurrencyTransactor, FungiblesTransactor);