diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d92349b1f..446b26e78 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -36,7 +36,6 @@ jobs: - name: Update run: | cargo update - cargo update base64ct --precise 1.6.0 # 1.8.0 requires the Cargo feature called `edition2024` cargo update pallet-revive-proc-macro --precise 0.3.0 # TODO: https://github.com/paritytech/polkadot-sdk/issues/9425 - name: Run clippy run: cargo clippy -- -D warnings diff --git a/asset-registry/src/mock/para.rs b/asset-registry/src/mock/para.rs index 6f7df77f2..880043a38 100644 --- a/asset-registry/src/mock/para.rs +++ b/asset-registry/src/mock/para.rs @@ -10,11 +10,9 @@ use frame_support::{ PalletId, }; use frame_system::{EnsureRoot, EnsureSignedBy}; -use orml_traits::{ - location::{AbsoluteReserveProvider, RelativeReserveProvider}, - parameter_type_with_key, FixedConversionRateProvider, MultiCurrency, -}; +use orml_traits::{parameter_type_with_key, FixedConversionRateProvider, MultiCurrency}; use orml_xcm_support::{IsNativeConcrete, MultiCurrencyAdapter, MultiNativeAsset}; +use orml_xtokens::{AbsoluteReserveProviderMigrationPhase, RelativeReserveProviderMigrationPhase}; use pallet_xcm::XcmPassthrough; use parity_scale_codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen}; use polkadot_parachain_primitives::primitives::Sibling; @@ -212,7 +210,7 @@ impl Config for XcmConfig { type XcmSender = XcmRouter; type AssetTransactor = LocalAssetTransactor; type OriginConverter = XcmOriginToCallOrigin; - type IsReserve = MultiNativeAsset; + type IsReserve = MultiNativeAsset>; type IsTeleporter = (); type UniversalLocation = UniversalLocation; type Barrier = Barrier; @@ -329,9 +327,10 @@ impl orml_xtokens::Config for Runtime { type BaseXcmWeight = BaseXcmWeight; type UniversalLocation = UniversalLocation; type MaxAssetsForTransfer = MaxAssetsForTransfer; - type ReserveProvider = RelativeReserveProvider; + type ReserveProvider = RelativeReserveProviderMigrationPhase; type RateLimiter = (); type RateLimiterId = (); + type MigrationPhaseUpdateOrigin = EnsureRoot; } impl orml_xcm::Config for Runtime { diff --git a/traits/src/location.rs b/traits/src/location.rs index 63496d25d..ed3b9dc5f 100644 --- a/traits/src/location.rs +++ b/traits/src/location.rs @@ -1,75 +1,13 @@ use sp_core::{bounded::BoundedVec, ConstU32}; use xcm::v5::prelude::*; -pub trait Parse { - /// Returns the "chain" location part. It could be parent, sibling - /// parachain, or child parachain. - fn chain_part(&self) -> Option; - /// Returns "non-chain" location part. - fn non_chain_part(&self) -> Option; -} - -fn is_chain_junction(junction: Option<&Junction>) -> bool { - matches!(junction, Some(Parachain(_))) -} - -impl Parse for Location { - fn chain_part(&self) -> Option { - match (self.parents, self.first_interior()) { - // sibling parachain - (1, Some(Parachain(id))) => Some(Location::new(1, [Parachain(*id)])), - // parent - (1, _) => Some(Location::parent()), - // children parachain - (0, Some(Parachain(id))) => Some(Location::new(0, [Parachain(*id)])), - _ => None, - } - } - - fn non_chain_part(&self) -> Option { - let mut junctions = self.interior().clone(); - while is_chain_junction(junctions.first()) { - let _ = junctions.take_first(); - } - - if junctions != Here { - Some(Location::new(0, junctions)) - } else { - None - } - } -} +pub const ASSET_HUB_ID: u32 = 1000; pub trait Reserve { /// Returns assets reserve location. fn reserve(asset: &Asset) -> Option; } -// Provide reserve in absolute path view -pub struct AbsoluteReserveProvider; - -impl Reserve for AbsoluteReserveProvider { - fn reserve(asset: &Asset) -> Option { - let AssetId(location) = &asset.id; - location.chain_part() - } -} - -// Provide reserve in relative path view -// Self tokens are represeneted as Here -pub struct RelativeReserveProvider; - -impl Reserve for RelativeReserveProvider { - fn reserve(asset: &Asset) -> Option { - let AssetId(location) = &asset.id; - if location.parents == 0 && !is_chain_junction(location.first_interior()) { - Some(Location::here()) - } else { - location.chain_part() - } - } -} - pub trait RelativeLocations { fn sibling_parachain_general_key(para_id: u32, general_key: BoundedVec>) -> Location; } @@ -79,93 +17,3 @@ impl RelativeLocations for Location { Location::new(1, [Parachain(para_id), general_key.as_bounded_slice().into()]) } } - -#[cfg(test)] -mod tests { - use super::*; - - const PARACHAIN: Junction = Parachain(1); - const GENERAL_INDEX: Junction = GeneralIndex(1); - - fn concrete_fungible(id: Location) -> Asset { - (id, 1).into() - } - - #[test] - fn parent_as_reserve_chain() { - assert_eq!( - AbsoluteReserveProvider::reserve(&concrete_fungible(Location::new(1, [GENERAL_INDEX]))), - Some(Location::parent()) - ); - assert_eq!( - RelativeReserveProvider::reserve(&concrete_fungible(Location::new(1, [GENERAL_INDEX]))), - Some(Location::parent()) - ); - } - - #[test] - fn sibling_parachain_as_reserve_chain() { - assert_eq!( - AbsoluteReserveProvider::reserve(&concrete_fungible(Location::new(1, [PARACHAIN, GENERAL_INDEX]))), - Some(Location::new(1, [PARACHAIN])) - ); - assert_eq!( - RelativeReserveProvider::reserve(&concrete_fungible(Location::new(1, [PARACHAIN, GENERAL_INDEX]))), - Some(Location::new(1, [PARACHAIN])) - ); - } - - #[test] - fn child_parachain_as_reserve_chain() { - assert_eq!( - AbsoluteReserveProvider::reserve(&concrete_fungible(Location::new(0, [PARACHAIN, GENERAL_INDEX]))), - Some(PARACHAIN.into()) - ); - assert_eq!( - RelativeReserveProvider::reserve(&concrete_fungible(Location::new(0, [PARACHAIN, GENERAL_INDEX]))), - Some(PARACHAIN.into()) - ); - } - - #[test] - fn no_reserve_chain_for_absolute_self_for_relative() { - assert_eq!( - AbsoluteReserveProvider::reserve(&concrete_fungible(Location::new( - 0, - [Junction::from(BoundedVec::try_from(b"DOT".to_vec()).unwrap())] - ))), - None - ); - assert_eq!( - RelativeReserveProvider::reserve(&concrete_fungible(Location::new( - 0, - [Junction::from(BoundedVec::try_from(b"DOT".to_vec()).unwrap())] - ))), - Some(Location::here()) - ); - } - - #[test] - fn non_chain_part_works() { - assert_eq!(Location::parent().non_chain_part(), None); - assert_eq!(Location::new(1, [PARACHAIN]).non_chain_part(), None); - assert_eq!(Location::new(0, [PARACHAIN]).non_chain_part(), None); - - assert_eq!( - Location::new(1, [GENERAL_INDEX]).non_chain_part(), - Some(GENERAL_INDEX.into()) - ); - assert_eq!( - Location::new(1, [GENERAL_INDEX, GENERAL_INDEX]).non_chain_part(), - Some((GENERAL_INDEX, GENERAL_INDEX).into()) - ); - assert_eq!( - Location::new(1, [PARACHAIN, GENERAL_INDEX]).non_chain_part(), - Some(GENERAL_INDEX.into()) - ); - assert_eq!( - Location::new(0, [PARACHAIN, GENERAL_INDEX]).non_chain_part(), - Some(GENERAL_INDEX.into()) - ); - } -} diff --git a/xcm-support/src/tests.rs b/xcm-support/src/tests.rs index 9b38d0360..aae80c4b1 100644 --- a/xcm-support/src/tests.rs +++ b/xcm-support/src/tests.rs @@ -4,7 +4,7 @@ use super::*; -use orml_traits::{location::AbsoluteReserveProvider, location::RelativeLocations, ConcreteFungibleAsset}; +use orml_traits::{location::RelativeLocations, ConcreteFungibleAsset}; #[derive(Debug, PartialEq, Eq)] pub enum TestCurrencyId { @@ -88,22 +88,3 @@ fn is_native_concrete_does_not_matches_non_native_currencies() { }) .is_none()); } - -#[test] -fn multi_native_asset() { - assert!(MultiNativeAsset::::contains( - &Asset { - fun: Fungible(10), - id: AssetId(Location::parent()) - }, - &Parent.into() - )); - assert!(MultiNativeAsset::::contains( - &Asset::sibling_parachain_asset(1, b"TokenA".to_vec().try_into().unwrap(), 100), - &Location::new(1, [Parachain(1)]), - )); - assert!(!MultiNativeAsset::::contains( - &Asset::sibling_parachain_asset(1, b"TokenA".to_vec().try_into().unwrap(), 100), - &Location::parent(), - )); -} diff --git a/xtokens/src/lib.rs b/xtokens/src/lib.rs index 9aa22afe7..5c1eafb60 100644 --- a/xtokens/src/lib.rs +++ b/xtokens/src/lib.rs @@ -53,7 +53,7 @@ use xcm_executor::traits::WeightBounds; pub use module::*; use orml_traits::{ - location::{Parse, Reserve}, + location::{Reserve, ASSET_HUB_ID}, xcm_transfer::{Transferred, XtokensWeightInfo}, GetByKey, RateLimiter, XcmTransfer, }; @@ -71,6 +71,19 @@ enum TransferKind { } use TransferKind::*; +#[derive( + scale_info::TypeInfo, Default, Encode, Decode, Clone, Eq, PartialEq, Debug, MaxEncodedLen, DecodeWithMemTracking, +)] +pub enum MigrationPhase { + /// Not started + #[default] + NotStarted, + /// Started + InProgress, + /// Completed + Completed, +} + #[frame_support::pallet] pub mod module { use super::*; @@ -135,8 +148,14 @@ pub mod module { /// The id of the RateLimiter. #[pallet::constant] type RateLimiterId: Get<::RateLimiterId>; + + /// The origin that can change the migration phase. + type MigrationPhaseUpdateOrigin: EnsureOrigin; } + #[pallet::storage] + pub type MigrationStatus = StorageValue<_, MigrationPhase, ValueQuery>; + #[pallet::event] #[pallet::generate_deposit(fn deposit_event)] pub enum Event { @@ -147,6 +166,8 @@ pub mod module { fee: Asset, dest: Location, }, + /// Migration phase changed. + MigrationPhaseChanged { migration_phase: MigrationPhase }, } #[pallet::error] @@ -394,6 +415,18 @@ pub mod module { Self::do_transfer_assets(who, assets.clone(), fee.clone(), dest, dest_weight_limit).map(|_| ()) } + + #[pallet::call_index(6)] + #[pallet::weight(frame_support::weights::Weight::from_parts(10000, 0))] + pub fn set_migration_phase(origin: OriginFor, migration_phase: MigrationPhase) -> DispatchResult { + T::MigrationPhaseUpdateOrigin::ensure_origin(origin)?; + + MigrationStatus::::set(migration_phase.clone()); + + Self::deposit_event(Event::::MigrationPhaseChanged { migration_phase }); + + Ok(()) + } } impl Pallet { @@ -565,7 +598,7 @@ pub mod module { if asset_len > 1 && fee_reserve != non_fee_reserve { // Current only support `ToReserve` with relay-chain asset as fee. other case // like `NonReserve` or `SelfReserve` with relay-chain fee is not support. - ensure!(non_fee_reserve == dest.chain_part(), Error::::InvalidAsset); + ensure!(non_fee_reserve == Self::chain_part(&dest), Error::::InvalidAsset); let reserve_location = non_fee_reserve.clone().ok_or(Error::::AssetHasNoReserve)?; let min_xcm_fee = T::MinXcmFee::get(&reserve_location).ok_or(Error::::MinXcmFeeNotDefined)?; @@ -590,7 +623,7 @@ pub mod module { let mut override_recipient = T::SelfLocation::get(); if override_recipient == Location::here() { - let dest_chain_part = dest.chain_part().ok_or(Error::::InvalidDest)?; + let dest_chain_part = Self::chain_part(&dest).ok_or(Error::::InvalidDest)?; let ancestry = T::UniversalLocation::get(); let _ = override_recipient .reanchor(&dest_chain_part, &ancestry) @@ -817,7 +850,7 @@ pub mod module { /// Ensure has the `dest` has chain part and recipient part. fn ensure_valid_dest(dest: &Location) -> Result<(Location, Location), DispatchError> { - if let (Some(dest), Some(recipient)) = (dest.chain_part(), dest.non_chain_part()) { + if let (Some(dest), Some(recipient)) = (Self::chain_part(dest), Self::non_chain_part(dest)) { Ok((dest, recipient)) } else { Err(Error::::InvalidDest.into()) @@ -863,6 +896,41 @@ pub mod module { let asset = assets.get(reserve_idx); asset.and_then(T::ReserveProvider::reserve) } + + /// Returns the "chain" location part. It could be parent, sibling + /// parachain, or child parachain. + pub fn chain_part(location: &Location) -> Option { + match (location.parents, location.first_interior()) { + // sibling parachain + (1, Some(Parachain(id))) => Some(Location::new(1, [Parachain(*id)])), + // parent + (1, _) => match MigrationStatus::::get() { + // RelayChain + MigrationPhase::NotStarted => Some(Location::parent()), + // Disable transfer when migration is in progress + MigrationPhase::InProgress => None, + // AssetHub + MigrationPhase::Completed => Some(Location::new(1, [Parachain(ASSET_HUB_ID)])), + }, + // children parachain + (0, Some(Parachain(id))) => Some(Location::new(0, [Parachain(*id)])), + _ => None, + } + } + + /// Returns "non-chain" location part. + pub fn non_chain_part(location: &Location) -> Option { + let mut junctions = location.interior().clone(); + while is_chain_junction(junctions.first()) { + let _ = junctions.take_first(); + } + + if junctions != Here { + Some(Location::new(0, junctions)) + } else { + None + } + } } pub struct XtokensWeight(PhantomData); @@ -1066,3 +1134,32 @@ fn subtract_fee(asset: &Asset, amount: u128) -> Asset { id: asset.id.clone(), } } + +fn is_chain_junction(junction: Option<&Junction>) -> bool { + matches!(junction, Some(Parachain(_))) +} + +// Provide reserve in absolute path view +pub struct AbsoluteReserveProviderMigrationPhase(PhantomData); + +impl Reserve for AbsoluteReserveProviderMigrationPhase { + fn reserve(asset: &Asset) -> Option { + let AssetId(location) = &asset.id; + Pallet::::chain_part(location) + } +} + +// Provide reserve in relative path view +// Self tokens are represeneted as Here +pub struct RelativeReserveProviderMigrationPhase(PhantomData); + +impl Reserve for RelativeReserveProviderMigrationPhase { + fn reserve(asset: &Asset) -> Option { + let AssetId(location) = &asset.id; + if location.parents == 0 && !is_chain_junction(location.first_interior()) { + Some(Location::here()) + } else { + Pallet::::chain_part(location) + } + } +} diff --git a/xtokens/src/mock/para.rs b/xtokens/src/mock/para.rs index 2482b0de5..3f11e3987 100644 --- a/xtokens/src/mock/para.rs +++ b/xtokens/src/mock/para.rs @@ -1,6 +1,6 @@ use super::{ - AllowTopLevelPaidExecution, Amount, Balance, CurrencyId, CurrencyIdConvert, ParachainXcmRouter, RateLimiter, - CHARLIE, + AbsoluteReserveProviderMigrationPhase, AllowTopLevelPaidExecution, Amount, Balance, CurrencyId, CurrencyIdConvert, + ParachainXcmRouter, RateLimiter, CHARLIE, }; use crate as orml_xtokens; @@ -26,10 +26,7 @@ use xcm_builder::{ use xcm_executor::{Config, XcmExecutor}; use crate::mock::AllTokensAreCreatedEqualToWeight; -use orml_traits::{ - location::{AbsoluteReserveProvider, Reserve}, - parameter_type_with_key, RateLimiterError, -}; +use orml_traits::{location::Reserve, parameter_type_with_key, RateLimiterError}; use orml_xcm_support::{IsNativeConcrete, MultiCurrencyAdapter}; pub type AccountId = AccountId32; @@ -145,7 +142,7 @@ impl Config for XcmConfig { type XcmSender = XcmRouter; type AssetTransactor = LocalAssetTransactor; type OriginConverter = XcmOriginToCallOrigin; - type IsReserve = MultiNativeAsset; + type IsReserve = MultiNativeAsset>; type IsTeleporter = NativeAsset; type UniversalLocation = UniversalLocation; type Barrier = Barrier; @@ -306,9 +303,10 @@ impl orml_xtokens::Config for Runtime { type BaseXcmWeight = BaseXcmWeight; type UniversalLocation = UniversalLocation; type MaxAssetsForTransfer = MaxAssetsForTransfer; - type ReserveProvider = AbsoluteReserveProvider; + type ReserveProvider = AbsoluteReserveProviderMigrationPhase; type RateLimiter = MockRateLimiter; type RateLimiterId = XtokensRateLimiterId; + type MigrationPhaseUpdateOrigin = EnsureRoot; } impl orml_xcm::Config for Runtime { diff --git a/xtokens/src/mock/para_relative_view.rs b/xtokens/src/mock/para_relative_view.rs index 70a679921..c1ff52a1f 100644 --- a/xtokens/src/mock/para_relative_view.rs +++ b/xtokens/src/mock/para_relative_view.rs @@ -1,4 +1,7 @@ -use super::{Amount, Balance, CurrencyId, CurrencyIdConvert, ParachainXcmRouter}; +use super::{ + AbsoluteReserveProviderMigrationPhase, Amount, Balance, CurrencyId, CurrencyIdConvert, ParachainXcmRouter, + RelativeReserveProviderMigrationPhase, +}; use crate as orml_xtokens; use frame_support::{ @@ -22,10 +25,7 @@ use xcm_builder::{ use xcm_executor::{Config, XcmExecutor}; use crate::mock::AllTokensAreCreatedEqualToWeight; -use orml_traits::{ - location::{AbsoluteReserveProvider, RelativeReserveProvider, Reserve}, - parameter_type_with_key, -}; +use orml_traits::{location::Reserve, parameter_type_with_key}; use orml_xcm_support::{IsNativeConcrete, MultiCurrencyAdapter}; pub type AccountId = AccountId32; @@ -141,7 +141,7 @@ impl Config for XcmConfig { type XcmSender = XcmRouter; type AssetTransactor = LocalAssetTransactor; type OriginConverter = XcmOriginToCallOrigin; - type IsReserve = MultiNativeAsset; + type IsReserve = MultiNativeAsset>; type IsTeleporter = (); type UniversalLocation = UniversalLocation; type Barrier = Barrier; @@ -378,9 +378,10 @@ impl orml_xtokens::Config for Runtime { type BaseXcmWeight = BaseXcmWeight; type UniversalLocation = UniversalLocation; type MaxAssetsForTransfer = MaxAssetsForTransfer; - type ReserveProvider = RelativeReserveProvider; + type ReserveProvider = RelativeReserveProviderMigrationPhase; type RateLimiter = (); type RateLimiterId = (); + type MigrationPhaseUpdateOrigin = EnsureRoot; } impl orml_xcm::Config for Runtime { diff --git a/xtokens/src/mock/para_teleport.rs b/xtokens/src/mock/para_teleport.rs index fb7e4efb8..a68090781 100644 --- a/xtokens/src/mock/para_teleport.rs +++ b/xtokens/src/mock/para_teleport.rs @@ -1,4 +1,7 @@ -use super::{AllowTopLevelPaidExecution, Amount, Balance, CurrencyId, CurrencyIdConvert, ParachainXcmRouter}; +use super::{ + AbsoluteReserveProviderMigrationPhase, AllowTopLevelPaidExecution, Amount, Balance, CurrencyId, CurrencyIdConvert, + ParachainXcmRouter, +}; use crate as orml_xtokens; use frame_support::{ @@ -22,7 +25,7 @@ use xcm_executor::{Config, XcmExecutor}; use crate::mock::teleport_currency_adapter::MultiTeleportCurrencyAdapter; use crate::mock::AllTokensAreCreatedEqualToWeight; -use orml_traits::{location::AbsoluteReserveProvider, parameter_type_with_key}; +use orml_traits::parameter_type_with_key; use orml_xcm_support::{DisabledParachainFee, IsNativeConcrete, MultiNativeAsset}; pub type AccountId = AccountId32; @@ -119,7 +122,7 @@ impl Config for XcmConfig { type XcmSender = XcmRouter; type AssetTransactor = LocalAssetTransactor; type OriginConverter = XcmOriginToCallOrigin; - type IsReserve = MultiNativeAsset; + type IsReserve = MultiNativeAsset>; type IsTeleporter = NativeAsset; type UniversalLocation = UniversalLocation; type Barrier = Barrier; @@ -226,9 +229,10 @@ impl orml_xtokens::Config for Runtime { type BaseXcmWeight = BaseXcmWeight; type UniversalLocation = UniversalLocation; type MaxAssetsForTransfer = MaxAssetsForTransfer; - type ReserveProvider = AbsoluteReserveProvider; + type ReserveProvider = AbsoluteReserveProviderMigrationPhase; type RateLimiter = (); type RateLimiterId = (); + type MigrationPhaseUpdateOrigin = EnsureRoot; } impl orml_xcm::Config for Runtime { diff --git a/xtokens/src/tests.rs b/xtokens/src/tests.rs index 5507b9687..8a65a33c7 100644 --- a/xtokens/src/tests.rs +++ b/xtokens/src/tests.rs @@ -2,9 +2,11 @@ use super::*; use cumulus_primitives_core::ParaId; +use frame_support::traits::ContainsPair; use frame_support::{assert_err, assert_noop, assert_ok, traits::Currency}; use mock::*; use orml_traits::{ConcreteFungibleAsset, MultiCurrency}; +use orml_xcm_support::MultiNativeAsset; use parity_scale_codec::Encode; use polkadot_parachain_primitives::primitives::Sibling; use sp_runtime::{traits::AccountIdConversion, AccountId32}; @@ -1929,3 +1931,181 @@ fn nfts_cannot_be_fee_assets() { ); }); } + +#[test] +fn set_migration_phase_should_work() { + TestNet::reset(); + + ParaA::execute_with(|| { + assert_eq!(MigrationPhase::NotStarted, MigrationStatus::::get()); + assert_ok!(ParaXTokens::set_migration_phase( + para::RuntimeOrigin::root(), + MigrationPhase::InProgress + )); + assert!(para::System::events().iter().any(|r| { + matches!( + r.event, + para::RuntimeEvent::XTokens(Event::MigrationPhaseChanged { + migration_phase: MigrationPhase::InProgress + }) + ) + })); + + assert_eq!(MigrationPhase::InProgress, MigrationStatus::::get()); + assert_ok!(ParaXTokens::set_migration_phase( + para::RuntimeOrigin::root(), + MigrationPhase::Completed + )); + assert_eq!(MigrationPhase::Completed, MigrationStatus::::get()); + }); +} + +// AbsoluteReserveProviderMigrationPhase and +// RelativeReserveProviderMigrationPhase +const PARACHAIN: Junction = Parachain(1); +const GENERAL_INDEX: Junction = GeneralIndex(1); + +fn concrete_fungible(id: Location) -> Asset { + (id, 1).into() +} + +#[test] +fn parent_as_reserve_chain() { + TestNet::reset(); + + ParaA::execute_with(|| { + assert_eq!( + AbsoluteReserveProviderMigrationPhase::::reserve(&concrete_fungible(Location::new( + 1, + [GENERAL_INDEX] + ))), + Some(Location::parent()) + ); + assert_eq!( + RelativeReserveProviderMigrationPhase::::reserve(&concrete_fungible(Location::new( + 1, + [GENERAL_INDEX] + ))), + Some(Location::parent()) + ); + }); +} + +#[test] +fn sibling_parachain_as_reserve_chain() { + TestNet::reset(); + + ParaA::execute_with(|| { + assert_eq!( + AbsoluteReserveProviderMigrationPhase::::reserve(&concrete_fungible(Location::new( + 1, + [PARACHAIN, GENERAL_INDEX] + ))), + Some(Location::new(1, [PARACHAIN])) + ); + assert_eq!( + RelativeReserveProviderMigrationPhase::::reserve(&concrete_fungible(Location::new( + 1, + [PARACHAIN, GENERAL_INDEX] + ))), + Some(Location::new(1, [PARACHAIN])) + ); + }); +} + +#[test] +fn child_parachain_as_reserve_chain() { + TestNet::reset(); + + ParaA::execute_with(|| { + assert_eq!( + AbsoluteReserveProviderMigrationPhase::::reserve(&concrete_fungible(Location::new( + 0, + [PARACHAIN, GENERAL_INDEX] + ))), + Some(PARACHAIN.into()) + ); + assert_eq!( + RelativeReserveProviderMigrationPhase::::reserve(&concrete_fungible(Location::new( + 0, + [PARACHAIN, GENERAL_INDEX] + ))), + Some(PARACHAIN.into()) + ); + }); +} + +#[test] +fn no_reserve_chain_for_absolute_self_for_relative() { + TestNet::reset(); + + ParaA::execute_with(|| { + assert_eq!( + AbsoluteReserveProviderMigrationPhase::::reserve(&concrete_fungible(Location::new( + 0, + [Junction::from(BoundedVec::try_from(b"DOT".to_vec()).unwrap())] + ))), + None + ); + assert_eq!( + RelativeReserveProviderMigrationPhase::::reserve(&concrete_fungible(Location::new( + 0, + [Junction::from(BoundedVec::try_from(b"DOT".to_vec()).unwrap())] + ))), + Some(Location::here()) + ); + }); +} + +#[test] +fn non_chain_part_works() { + assert_eq!(ParaXTokens::non_chain_part(&Location::parent()), None); + assert_eq!(ParaXTokens::non_chain_part(&Location::new(1, [PARACHAIN])), None); + assert_eq!(ParaXTokens::non_chain_part(&Location::new(0, [PARACHAIN])), None); + + assert_eq!( + ParaXTokens::non_chain_part(&Location::new(1, [GENERAL_INDEX])), + Some(GENERAL_INDEX.into()) + ); + assert_eq!( + ParaXTokens::non_chain_part(&Location::new(1, [GENERAL_INDEX, GENERAL_INDEX])), + Some((GENERAL_INDEX, GENERAL_INDEX).into()) + ); + assert_eq!( + ParaXTokens::non_chain_part(&Location::new(1, [PARACHAIN, GENERAL_INDEX])), + Some(GENERAL_INDEX.into()) + ); + assert_eq!( + ParaXTokens::non_chain_part(&Location::new(0, [PARACHAIN, GENERAL_INDEX])), + Some(GENERAL_INDEX.into()) + ); +} + +#[test] +fn multi_native_asset() { + TestNet::reset(); + + ParaA::execute_with(|| { + assert!( + MultiNativeAsset::>::contains( + &Asset { + fun: Fungible(10), + id: AssetId(Location::parent()) + }, + &Parent.into() + ) + ); + assert!( + MultiNativeAsset::>::contains( + &Asset::sibling_parachain_asset(1, b"TokenA".to_vec().try_into().unwrap(), 100), + &Location::new(1, [Parachain(1)]), + ) + ); + assert!( + !MultiNativeAsset::>::contains( + &Asset::sibling_parachain_asset(1, b"TokenA".to_vec().try_into().unwrap(), 100), + &Location::parent(), + ) + ); + }); +}