Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
add MigrationPhase
  • Loading branch information
zjb0807 committed Sep 25, 2025
commit 27bfc475fb897f3ecd40478a3b3426fde1ad1314
64 changes: 1 addition & 63 deletions traits/src/location.rs
Original file line number Diff line number Diff line change
@@ -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<Location>;
/// Returns "non-chain" location part.
fn non_chain_part(&self) -> Option<Location>;
}

fn is_chain_junction(junction: Option<&Junction>) -> bool {
matches!(junction, Some(Parachain(_)))
}

impl Parse for Location {
fn chain_part(&self) -> Option<Location> {
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<Location> {
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<Location>;
}

// Provide reserve in absolute path view
pub struct AbsoluteReserveProvider;

impl Reserve for AbsoluteReserveProvider {
fn reserve(asset: &Asset) -> Option<Location> {
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<Location> {
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<u8, ConstU32<32>>) -> Location;
}
Expand Down
105 changes: 101 additions & 4 deletions xtokens/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand All @@ -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::*;
Expand Down Expand Up @@ -135,8 +148,14 @@ pub mod module {
/// The id of the RateLimiter.
#[pallet::constant]
type RateLimiterId: Get<<Self::RateLimiter as RateLimiter>::RateLimiterId>;

/// The origin that can change the migration phase.
type MigrationPhaseUpdateOrigin: EnsureOrigin<Self::RuntimeOrigin>;
}

#[pallet::storage]
pub type MigrationStatus<T: Config> = StorageValue<_, MigrationPhase, ValueQuery>;

#[pallet::event]
#[pallet::generate_deposit(fn deposit_event)]
pub enum Event<T: Config> {
Expand All @@ -147,6 +166,8 @@ pub mod module {
fee: Asset,
dest: Location,
},
/// Migration phase changed.
MigrationPhaseChanged { migration_phase: MigrationPhase },
}

#[pallet::error]
Expand Down Expand Up @@ -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<T>, migration_phase: MigrationPhase) -> DispatchResult {
T::MigrationPhaseUpdateOrigin::ensure_origin(origin)?;

MigrationStatus::<T>::set(migration_phase.clone());

Self::deposit_event(Event::<T>::MigrationPhaseChanged { migration_phase });

Ok(())
}
}

impl<T: Config> Pallet<T> {
Expand Down Expand Up @@ -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::<T>::InvalidAsset);
ensure!(non_fee_reserve == Self::chain_part(&dest), Error::<T>::InvalidAsset);

let reserve_location = non_fee_reserve.clone().ok_or(Error::<T>::AssetHasNoReserve)?;
let min_xcm_fee = T::MinXcmFee::get(&reserve_location).ok_or(Error::<T>::MinXcmFeeNotDefined)?;
Expand All @@ -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::<T>::InvalidDest)?;
let dest_chain_part = Self::chain_part(&dest).ok_or(Error::<T>::InvalidDest)?;
let ancestry = T::UniversalLocation::get();
let _ = override_recipient
.reanchor(&dest_chain_part, &ancestry)
Expand Down Expand Up @@ -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::<T>::InvalidDest.into())
Expand Down Expand Up @@ -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<Location> {
match (location.parents, location.first_interior()) {
// sibling parachain
(1, Some(Parachain(id))) => Some(Location::new(1, [Parachain(*id)])),
// parent
(1, _) => match MigrationStatus::<T>::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<Location> {
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<T>(PhantomData<T>);
Expand Down Expand Up @@ -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<T>(PhantomData<T>);

impl<T: Config> Reserve for AbsoluteReserveProviderMigrationPhase<T> {
fn reserve(asset: &Asset) -> Option<Location> {
let AssetId(location) = &asset.id;
Pallet::<T>::chain_part(location)
}
}

// Provide reserve in relative path view
// Self tokens are represeneted as Here
pub struct RelativeReserveProviderMigrationPhase<T>(PhantomData<T>);

impl<T: Config> Reserve for RelativeReserveProviderMigrationPhase<T> {
fn reserve(asset: &Asset) -> Option<Location> {
let AssetId(location) = &asset.id;
if location.parents == 0 && !is_chain_junction(location.first_interior()) {
Some(Location::here())
} else {
Pallet::<T>::chain_part(location)
}
}
}
14 changes: 6 additions & 8 deletions xtokens/src/mock/para.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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;
Expand Down Expand Up @@ -145,7 +142,7 @@ impl Config for XcmConfig {
type XcmSender = XcmRouter;
type AssetTransactor = LocalAssetTransactor;
type OriginConverter = XcmOriginToCallOrigin;
type IsReserve = MultiNativeAsset<AbsoluteReserveProvider>;
type IsReserve = MultiNativeAsset<AbsoluteReserveProviderMigrationPhase<Runtime>>;
type IsTeleporter = NativeAsset;
type UniversalLocation = UniversalLocation;
type Barrier = Barrier;
Expand Down Expand Up @@ -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<Runtime>;
type RateLimiter = MockRateLimiter;
type RateLimiterId = XtokensRateLimiterId;
type MigrationPhaseUpdateOrigin = EnsureRoot<AccountId>;
}

impl orml_xcm::Config for Runtime {
Expand Down
15 changes: 8 additions & 7 deletions xtokens/src/mock/para_relative_view.rs
Original file line number Diff line number Diff line change
@@ -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::{
Expand All @@ -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;
Expand Down Expand Up @@ -141,7 +141,7 @@ impl Config for XcmConfig {
type XcmSender = XcmRouter;
type AssetTransactor = LocalAssetTransactor;
type OriginConverter = XcmOriginToCallOrigin;
type IsReserve = MultiNativeAsset<AbsoluteReserveProvider>;
type IsReserve = MultiNativeAsset<AbsoluteReserveProviderMigrationPhase<Runtime>>;
type IsTeleporter = ();
type UniversalLocation = UniversalLocation;
type Barrier = Barrier;
Expand Down Expand Up @@ -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<Runtime>;
type RateLimiter = ();
type RateLimiterId = ();
type MigrationPhaseUpdateOrigin = EnsureRoot<AccountId>;
}

impl orml_xcm::Config for Runtime {
Expand Down
12 changes: 8 additions & 4 deletions xtokens/src/mock/para_teleport.rs
Original file line number Diff line number Diff line change
@@ -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::{
Expand All @@ -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;
Expand Down Expand Up @@ -119,7 +122,7 @@ impl Config for XcmConfig {
type XcmSender = XcmRouter;
type AssetTransactor = LocalAssetTransactor;
type OriginConverter = XcmOriginToCallOrigin;
type IsReserve = MultiNativeAsset<AbsoluteReserveProvider>;
type IsReserve = MultiNativeAsset<AbsoluteReserveProviderMigrationPhase<Runtime>>;
type IsTeleporter = NativeAsset;
type UniversalLocation = UniversalLocation;
type Barrier = Barrier;
Expand Down Expand Up @@ -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<Runtime>;
type RateLimiter = ();
type RateLimiterId = ();
type MigrationPhaseUpdateOrigin = EnsureRoot<AccountId>;
}

impl orml_xcm::Config for Runtime {
Expand Down
Loading