@@ -54,14 +54,18 @@ use xcm_executor::traits::WeightBounds;
5454
5555pub use module:: * ;
5656use orml_traits:: {
57- location:: { Parse , Reserve } ,
57+ location:: Reserve ,
5858 xcm_transfer:: { Transferred , XtokensWeightInfo } ,
5959 GetByKey , XcmTransfer ,
6060} ;
6161
62+ use codec:: DecodeWithMemTracking ;
63+
6264mod mock;
6365mod tests;
6466
67+ pub const ASSET_HUB_ID : u32 = 1_000 ;
68+
6569enum TransferKind {
6670 /// Transfer self reserve asset.
6771 SelfReserveAsset ,
@@ -72,6 +76,29 @@ enum TransferKind {
7276}
7377use TransferKind :: * ;
7478
79+ #[ derive(
80+ scale_info:: TypeInfo ,
81+ Default ,
82+ Encode ,
83+ Decode ,
84+ Clone ,
85+ Copy ,
86+ Eq ,
87+ PartialEq ,
88+ Debug ,
89+ MaxEncodedLen ,
90+ DecodeWithMemTracking ,
91+ ) ]
92+ pub enum MigrationPhase {
93+ /// Not started
94+ #[ default]
95+ NotStarted ,
96+ /// Started
97+ InProgress ,
98+ /// Completed
99+ Completed ,
100+ }
101+
75102#[ frame_support:: pallet]
76103pub mod module {
77104 use super :: * ;
@@ -131,8 +158,14 @@ pub mod module {
131158 /// The way to retreave the reserve of a MultiAsset. This can be
132159 /// configured to accept absolute or relative paths for self tokens
133160 type ReserveProvider : Reserve ;
161+
162+ /// The origin that can change the migration phase
163+ type MigrationPhaseUpdateOrigin : EnsureOrigin < Self :: RuntimeOrigin > ;
134164 }
135165
166+ #[ pallet:: storage]
167+ pub type MigrationStatus < T : Config > = StorageValue < _ , MigrationPhase , ValueQuery > ;
168+
136169 #[ pallet:: event]
137170 #[ pallet:: generate_deposit( fn deposit_event) ]
138171 pub enum Event < T : Config > {
@@ -143,6 +176,8 @@ pub mod module {
143176 fee : MultiAsset ,
144177 dest : MultiLocation ,
145178 } ,
179+ /// The AH migration phase has changed
180+ MigrationPhaseChanged { migration_phase : MigrationPhase } ,
146181 }
147182
148183 #[ pallet:: error]
@@ -388,6 +423,21 @@ pub mod module {
388423
389424 Self :: do_transfer_multiassets ( who, assets. clone ( ) , fee. clone ( ) , dest, dest_weight_limit) . map ( |_| ( ) )
390425 }
426+
427+ /// Must be called by `[T::MigrationPhaseUpdateOrigin]`.
428+ /// Changes the AH migration status, which will have impact on how
429+ /// reserves are computed from this pallet extrinsics.
430+ #[ pallet:: call_index( 6 ) ]
431+ #[ pallet:: weight( frame_support:: weights:: Weight :: from_parts( 10000 , 0 ) ) ]
432+ pub fn set_migration_phase ( origin : OriginFor < T > , migration_phase : MigrationPhase ) -> DispatchResult {
433+ T :: MigrationPhaseUpdateOrigin :: ensure_origin ( origin) ?;
434+
435+ MigrationStatus :: < T > :: set ( migration_phase) ;
436+
437+ Self :: deposit_event ( Event :: < T > :: MigrationPhaseChanged { migration_phase } ) ;
438+
439+ Ok ( ( ) )
440+ }
391441 }
392442
393443 impl < T : Config > Pallet < T > {
@@ -548,7 +598,7 @@ pub mod module {
548598 if fee_reserve != non_fee_reserve {
549599 // Current only support `ToReserve` with relay-chain asset as fee. other case
550600 // like `NonReserve` or `SelfReserve` with relay-chain fee is not support.
551- ensure ! ( non_fee_reserve == dest . chain_part( ) , Error :: <T >:: InvalidAsset ) ;
601+ ensure ! ( non_fee_reserve == Self :: chain_part( & dest ) , Error :: <T >:: InvalidAsset ) ;
552602
553603 let reserve_location = non_fee_reserve. ok_or ( Error :: < T > :: AssetHasNoReserve ) ?;
554604 let min_xcm_fee = T :: MinXcmFee :: get ( & reserve_location) . ok_or ( Error :: < T > :: MinXcmFeeNotDefined ) ?;
@@ -573,7 +623,7 @@ pub mod module {
573623
574624 let mut override_recipient = T :: SelfLocation :: get ( ) ;
575625 if override_recipient == MultiLocation :: here ( ) {
576- let dest_chain_part = dest . chain_part ( ) . ok_or ( Error :: < T > :: InvalidDest ) ?;
626+ let dest_chain_part = Self :: chain_part ( & dest ) . ok_or ( Error :: < T > :: InvalidDest ) ?;
577627 let ancestry = T :: UniversalLocation :: get ( ) ;
578628 let _ = override_recipient
579629 . reanchor ( & dest_chain_part, ancestry)
@@ -798,7 +848,7 @@ pub mod module {
798848
799849 /// Ensure has the `dest` has chain part and recipient part.
800850 fn ensure_valid_dest ( dest : & MultiLocation ) -> Result < ( MultiLocation , MultiLocation ) , DispatchError > {
801- if let ( Some ( dest) , Some ( recipient) ) = ( dest . chain_part ( ) , dest . non_chain_part ( ) ) {
851+ if let ( Some ( dest) , Some ( recipient) ) = ( Self :: chain_part ( & dest ) , Self :: non_chain_part ( & dest ) ) {
802852 Ok ( ( dest, recipient) )
803853 } else {
804854 Err ( Error :: < T > :: InvalidDest . into ( ) )
@@ -844,6 +894,41 @@ pub mod module {
844894 let asset = assets. get ( reserve_idx) ;
845895 asset. and_then ( T :: ReserveProvider :: reserve)
846896 }
897+
898+ /// Returns the "chain" location part. It could be parent, sibling
899+ /// parachain, or child parachain.
900+ pub fn chain_part ( location : & MultiLocation ) -> Option < MultiLocation > {
901+ match ( location. parents , location. first_interior ( ) ) {
902+ // sibling parachain
903+ ( 1 , Some ( Parachain ( id) ) ) => Some ( MultiLocation :: new ( 1 , Parachain ( * id) ) ) ,
904+ // parent
905+ ( 1 , _) => match MigrationStatus :: < T > :: get ( ) {
906+ // RelayChain
907+ MigrationPhase :: NotStarted => Some ( MultiLocation :: parent ( ) ) ,
908+ // Disable transfer when migration is in progress
909+ MigrationPhase :: InProgress => None ,
910+ // AssetHub
911+ MigrationPhase :: Completed => Some ( MultiLocation :: new ( 1 , Parachain ( ASSET_HUB_ID ) ) ) ,
912+ } ,
913+ // children parachain
914+ ( 0 , Some ( Parachain ( id) ) ) => Some ( MultiLocation :: new ( 0 , Parachain ( * id) ) ) ,
915+ _ => None ,
916+ }
917+ }
918+
919+ /// Returns "non-chain" location part.
920+ pub fn non_chain_part ( location : & MultiLocation ) -> Option < MultiLocation > {
921+ let mut junctions = location. interior ( ) . clone ( ) ;
922+ while is_chain_junction ( junctions. first ( ) ) {
923+ let _ = junctions. take_first ( ) ;
924+ }
925+
926+ if junctions != Here {
927+ Some ( MultiLocation :: new ( 0 , junctions) )
928+ } else {
929+ None
930+ }
931+ }
847932 }
848933
849934 pub struct XtokensWeight < T > ( PhantomData < T > ) ;
@@ -1039,3 +1124,40 @@ fn subtract_fee(asset: &MultiAsset, amount: u128) -> MultiAsset {
10391124 id : asset. id ,
10401125 }
10411126}
1127+
1128+ fn is_chain_junction ( junction : Option < & Junction > ) -> bool {
1129+ matches ! ( junction, Some ( Parachain ( _) ) )
1130+ }
1131+
1132+ // Provide reserve in absolute path view
1133+ pub struct AbsoluteReserveProviderMigrationPhase < T > ( PhantomData < T > ) ;
1134+
1135+ impl < T : Config > Reserve for AbsoluteReserveProviderMigrationPhase < T > {
1136+ fn reserve ( asset : & MultiAsset ) -> Option < MultiLocation > {
1137+ let location = if let AssetId :: Concrete ( location) = & asset. id {
1138+ location
1139+ } else {
1140+ return None ;
1141+ } ;
1142+ Pallet :: < T > :: chain_part ( location)
1143+ }
1144+ }
1145+
1146+ // Provide reserve in relative path view
1147+ // Self tokens are represeneted as Here
1148+ pub struct RelativeReserveProviderMigrationPhase < T > ( PhantomData < T > ) ;
1149+
1150+ impl < T : Config > Reserve for RelativeReserveProviderMigrationPhase < T > {
1151+ fn reserve ( asset : & MultiAsset ) -> Option < MultiLocation > {
1152+ let location = if let AssetId :: Concrete ( location) = & asset. id {
1153+ location
1154+ } else {
1155+ return None ;
1156+ } ;
1157+ if location. parents == 0 && !is_chain_junction ( location. first_interior ( ) ) {
1158+ Some ( MultiLocation :: here ( ) )
1159+ } else {
1160+ Pallet :: < T > :: chain_part ( location)
1161+ }
1162+ }
1163+ }
0 commit comments