moonbeam_runtime/
xcm_config.rs

1// Copyright 2019-2025 PureStake Inc.
2// This file is part of Moonbeam.
3
4// Moonbeam is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Moonbeam is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Moonbeam.  If not, see <http://www.gnu.org/licenses/>.
16
17//! XCM configuration for Moonbase.
18//!
19
20use super::{
21	bridge_config, governance, AccountId, AssetId, Balance, Balances, BridgeXcmOverMoonriver,
22	EmergencyParaXcm, Erc20XcmBridge, EvmForeignAssets, MaintenanceMode, MessageQueue,
23	OpenTechCommitteeInstance, ParachainInfo, ParachainSystem, Perbill, PolkadotXcm, Runtime,
24	RuntimeBlockWeights, RuntimeCall, RuntimeEvent, RuntimeOrigin, Treasury, XcmpQueue,
25};
26
27use super::moonbeam_weights;
28use frame_support::{
29	parameter_types,
30	traits::{EitherOf, EitherOfDiverse, Everything, Nothing, PalletInfoAccess, TransformOrigin},
31};
32use moonkit_xcm_primitives::AccountIdAssetIdConversion;
33use sp_runtime::{
34	traits::{Hash as THash, MaybeEquivalence, PostDispatchInfoOf},
35	DispatchErrorWithPostInfo,
36};
37use sp_weights::Weight;
38
39use frame_system::{EnsureRoot, RawOrigin};
40use sp_core::{ConstU32, H160, H256};
41
42use xcm_builder::{
43	AccountKey20Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom,
44	AllowTopLevelPaidExecutionFrom, Case, DescribeAllTerminal, DescribeFamily, EnsureXcmOrigin,
45	ExternalConsensusLocationsConverterFor, FungibleAdapter as XcmCurrencyAdapter,
46	HashedDescription, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative,
47	SiblingParachainConvertsVia, SignedAccountKey20AsNative, SovereignSignedViaLocation,
48	TakeWeightCredit, TrailingSetTopicAsId, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic,
49};
50
51use parachains_common::message_queue::{NarrowOriginToSibling, ParaIdToSibling};
52use xcm::{
53	latest::prelude::{
54		AllOf, Asset, AssetFilter, GlobalConsensus, InteriorLocation, Junction, Location,
55		NetworkId, PalletInstance, Parachain, Wild, WildFungible,
56	},
57	IntoVersion,
58};
59
60use xcm_executor::traits::{CallDispatcher, ConvertLocation};
61
62use cumulus_primitives_core::{AggregateMessageOrigin, ParaId};
63use frame_support::traits::Disabled;
64use pallet_xcm::EnsureXcm;
65use xcm_primitives::{
66	AbsoluteAndRelativeReserve, AccountIdToCurrencyId, AccountIdToLocation,
67	IsBridgedConcreteAssetFrom, MultiNativeAsset, SignedToAccountId20, XcmTransact,
68};
69
70use crate::governance::referenda::{FastGeneralAdminOrRoot, GeneralAdminOrRoot};
71use crate::runtime_params::dynamic_params;
72use moonbeam_runtime_common::xcm_origins::AllowSiblingParachains;
73use pallet_moonbeam_foreign_assets::{MapSuccessToGovernance, MapSuccessToXcm};
74use parity_scale_codec::{Decode, DecodeWithMemTracking, Encode};
75use scale_info::TypeInfo;
76use sp_core::Get;
77use sp_std::{
78	convert::{From, Into, TryFrom},
79	prelude::*,
80};
81
82parameter_types! {
83	// The network Id of the relay
84	pub const RelayNetwork: NetworkId = NetworkId::Polkadot;
85	// The relay chain Origin type
86	pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into();
87	pub UniversalLocation: InteriorLocation =
88		[GlobalConsensus(RelayNetwork::get()), Parachain(ParachainInfo::parachain_id().into())].into();
89	// Self Reserve location, defines the multilocation identifiying the self-reserve currency
90	// This is used to match it also against our Balances pallet when we receive such
91	// a Location: (Self Balances pallet index)
92	// We use the RELATIVE multilocation
93	pub SelfReserve: Location = Location {
94		parents:0,
95		interior: [
96			PalletInstance(<Balances as PalletInfoAccess>::index() as u8)
97		].into()
98	};
99}
100
101/// Type for specifying how a `Location` can be converted into an `AccountId`. This is used
102/// when determining ownership of accounts for asset transacting and when attempting to use XCM
103/// `Transact` in order to determine the dispatch Origin.
104pub type LocationToAccountId = (
105	// The parent (Relay-chain) origin converts to the default `AccountId`.
106	ParentIsPreset<AccountId>,
107	// Sibling parachain origins convert to AccountId via the `ParaId::into`.
108	SiblingParachainConvertsVia<polkadot_parachain::primitives::Sibling, AccountId>,
109	// If we receive a Location of type AccountKey20, just generate a native account
110	AccountKey20Aliases<RelayNetwork, AccountId>,
111	// Foreign locations alias into accounts according to a hash of their standard description.
112	HashedDescription<AccountId, DescribeFamily<DescribeAllTerminal>>,
113	// Different global consensus parachain sovereign account.
114	// (Used for over-bridge transfers and reserve processing)
115	ExternalConsensusLocationsConverterFor<UniversalLocation, AccountId>,
116);
117
118/// Wrapper type around `LocationToAccountId` to convert an `AccountId` to type `H160`.
119pub struct LocationToH160;
120impl ConvertLocation<H160> for LocationToH160 {
121	fn convert_location(location: &Location) -> Option<H160> {
122		<LocationToAccountId as ConvertLocation<AccountId>>::convert_location(location)
123			.map(Into::into)
124	}
125}
126
127/// The transactor for our own chain currency.
128pub type LocalAssetTransactor = XcmCurrencyAdapter<
129	// Use this currency:
130	Balances,
131	// Use this currency when it is a fungible asset matching any of the locations in
132	// SelfReserveRepresentations
133	xcm_builder::IsConcrete<SelfReserve>,
134	// We can convert the MultiLocations with our converter above:
135	LocationToAccountId,
136	// Our chain's account ID type (we can't get away without mentioning it explicitly):
137	AccountId,
138	// We dont allow teleport
139	(),
140>;
141
142// We use all transactors
143pub type AssetTransactors = (LocalAssetTransactor, EvmForeignAssets, Erc20XcmBridge);
144
145/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance,
146/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can
147/// biases the kind of local `Origin` it will become.
148pub type XcmOriginToTransactDispatchOrigin = (
149	// Sovereign account converter; this attempts to derive an `AccountId` from the origin location
150	// using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for
151	// foreign chains who want to have a local sovereign account on this chain which they control.
152	SovereignSignedViaLocation<LocationToAccountId, RuntimeOrigin>,
153	// Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when
154	// recognised.
155	RelayChainAsNative<RelayChainOrigin, RuntimeOrigin>,
156	// Native converter for sibling Parachains; will convert to a `SiblingPara` origin when
157	// recognised.
158	SiblingParachainAsNative<cumulus_pallet_xcm::Origin, RuntimeOrigin>,
159	// Xcm origins can be represented natively under the Xcm pallet's Xcm origin.
160	pallet_xcm::XcmPassthrough<RuntimeOrigin>,
161	// Xcm Origins defined by a Multilocation of type AccountKey20 can be converted to a 20 byte-
162	// account local origin
163	SignedAccountKey20AsNative<RelayNetwork, RuntimeOrigin>,
164);
165
166parameter_types! {
167	/// Maximum number of instructions in a single XCM fragment. A sanity check against
168	/// weight caculations getting too crazy.
169	pub MaxInstructions: u32 = 100;
170}
171
172/// Xcm Weigher shared between multiple Xcm-related configs.
173pub type XcmWeigher = WeightInfoBounds<
174	crate::weights::xcm::XcmWeight<Runtime, RuntimeCall>,
175	RuntimeCall,
176	MaxInstructions,
177>;
178
179pub type XcmBarrier = TrailingSetTopicAsId<(
180	// Weight that is paid for may be consumed.
181	TakeWeightCredit,
182	// Expected responses are OK.
183	AllowKnownQueryResponses<PolkadotXcm>,
184	WithComputedOrigin<
185		(
186			// If the message is one that immediately attemps to pay for execution, then allow it.
187			AllowTopLevelPaidExecutionFrom<Everything>,
188			// Subscriptions for version tracking are OK.
189			AllowSubscriptionsFrom<Everything>,
190		),
191		UniversalLocation,
192		ConstU32<8>,
193	>,
194)>;
195
196parameter_types! {
197	/// Xcm fees will go to the treasury account
198	pub XcmFeesAccount: AccountId = Treasury::account_id();
199}
200
201pub struct SafeCallFilter;
202impl frame_support::traits::Contains<RuntimeCall> for SafeCallFilter {
203	fn contains(_call: &RuntimeCall) -> bool {
204		// TODO review
205		// This needs to be addressed at EVM level
206		true
207	}
208}
209
210parameter_types! {
211	 /// Location of Asset Hub
212	pub AssetHubLocation: Location = Location::new(1, [Parachain(1000)]);
213	pub const RelayLocation: Location = Location::parent();
214	pub RelayLocationFilter: AssetFilter = Wild(AllOf {
215		fun: WildFungible,
216		id: xcm::prelude::AssetId(RelayLocation::get()),
217	});
218	pub RelayChainNativeAssetFromAssetHub: (AssetFilter, Location) = (
219		RelayLocationFilter::get(),
220		AssetHubLocation::get()
221	);
222	pub const MaxAssetsIntoHolding: u32 = xcm_primitives::MAX_ASSETS;
223}
224
225type Reserves = (
226	// Assets bridged from different consensus systems held in reserve on Asset Hub.
227	IsBridgedConcreteAssetFrom<AssetHubLocation>,
228	// Assets bridged from Moonriver
229	IsBridgedConcreteAssetFrom<bp_moonriver::GlobalConsensusLocation>,
230	// Relaychain (DOT) from Asset Hub
231	Case<RelayChainNativeAssetFromAssetHub>,
232	// Assets which the reserve is the same as the origin.
233	MultiNativeAsset<AbsoluteAndRelativeReserve<SelfLocationAbsolute>>,
234);
235
236// Our implementation of the Moonbeam Call
237// Attaches the right origin in case the call is made to pallet-ethereum-xcm
238#[cfg(not(feature = "evm-tracing"))]
239moonbeam_runtime_common::impl_moonbeam_xcm_call!();
240#[cfg(feature = "evm-tracing")]
241moonbeam_runtime_common::impl_moonbeam_xcm_call_tracing!();
242
243moonbeam_runtime_common::impl_evm_runner_precompile_or_eth_xcm!();
244
245pub struct XcmExecutorConfig;
246impl xcm_executor::Config for XcmExecutorConfig {
247	type RuntimeCall = RuntimeCall;
248	type XcmSender = XcmRouter;
249	// How to withdraw and deposit an asset.
250	type AssetTransactor = AssetTransactors;
251	type OriginConverter = XcmOriginToTransactDispatchOrigin;
252	// Filter to the reserve withdraw operations
253	// Whenever the reserve matches the relative or absolute value
254	// of our chain, we always return the relative reserve
255	type IsReserve = Reserves;
256	type IsTeleporter = (); // No teleport
257	type UniversalLocation = UniversalLocation;
258	type Barrier = XcmBarrier;
259	type Weigher = XcmWeigher;
260	// As trader we use the XcmWeightTrader pallet.
261	// For each foreign asset, the fee is computed based on its relative price (also
262	// stored in the XcmWeightTrader pallet) against the native asset.
263	// For the native asset fee is computed using WeightToFee implementation.
264	type Trader = pallet_xcm_weight_trader::Trader<Runtime>;
265	type ResponseHandler = PolkadotXcm;
266	type SubscriptionService = PolkadotXcm;
267	type AssetTrap = pallet_erc20_xcm_bridge::AssetTrapWrapper<PolkadotXcm, Runtime>;
268	type AssetClaims = PolkadotXcm;
269	type CallDispatcher = MoonbeamCall;
270	type PalletInstancesInfo = crate::AllPalletsWithSystem;
271	type MaxAssetsIntoHolding = MaxAssetsIntoHolding;
272	type AssetLocker = ();
273	type AssetExchanger = ();
274	type FeeManager = ();
275	type MessageExporter = BridgeXcmOverMoonriver;
276	type UniversalAliases = bridge_config::UniversalAliases;
277	type SafeCallFilter = SafeCallFilter;
278	type Aliasers = Nothing;
279	type TransactionalProcessor = xcm_builder::FrameTransactionalProcessor;
280	type HrmpNewChannelOpenRequestHandler = ();
281	type HrmpChannelAcceptedHandler = ();
282	type HrmpChannelClosingHandler = ();
283	type XcmRecorder = PolkadotXcm;
284	type XcmEventEmitter = PolkadotXcm;
285}
286
287pub type XcmExecutor = pallet_erc20_xcm_bridge::XcmExecutorWrapper<
288	XcmExecutorConfig,
289	xcm_executor::XcmExecutor<XcmExecutorConfig>,
290>;
291
292// Converts a Signed Local Origin into a Location
293pub type LocalOriginToLocation = SignedToAccountId20<RuntimeOrigin, AccountId, RelayNetwork>;
294
295/// For routing XCM messages which do not cross local consensus boundary.
296pub type LocalXcmRouter = (
297	// Two routers - use UMP to communicate with the relay chain:
298	cumulus_primitives_utility::ParentAsUmp<ParachainSystem, PolkadotXcm, ()>,
299	// ..and XCMP to communicate with the sibling chains.
300	XcmpQueue,
301);
302
303/// The means for routing XCM messages which are not for local execution into the right message
304/// queues.
305pub type XcmRouter = WithUniqueTopic<(
306	// The means for routing XCM messages which are not for local execution into the right message
307	// queues.
308	LocalXcmRouter,
309	// Router that exports messages to be delivered to the Kusama GlobalConsensus
310	moonbeam_runtime_common::bridge::BridgeXcmRouter<
311		xcm_builder::LocalExporter<BridgeXcmOverMoonriver, UniversalLocation>,
312	>,
313)>;
314
315impl pallet_xcm::Config for Runtime {
316	type RuntimeEvent = RuntimeEvent;
317	type SendXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
318	type XcmRouter = XcmRouter;
319	type ExecuteXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
320	type XcmExecuteFilter = Nothing;
321	type XcmExecutor = XcmExecutor;
322	type XcmTeleportFilter = Nothing;
323	type XcmReserveTransferFilter = Everything;
324	type Weigher = XcmWeigher;
325	type UniversalLocation = UniversalLocation;
326	type RuntimeOrigin = RuntimeOrigin;
327	type RuntimeCall = RuntimeCall;
328	const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
329	type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
330	type Currency = Balances;
331	type CurrencyMatcher = ();
332	type TrustedLockers = ();
333	type SovereignAccountOf = LocationToAccountId;
334	type MaxLockers = ConstU32<8>;
335	type MaxRemoteLockConsumers = ConstU32<0>;
336	type RemoteLockConsumerIdentifier = ();
337	type WeightInfo = moonbeam_weights::pallet_xcm::WeightInfo<Runtime>;
338	type AdminOrigin = EnsureRoot<AccountId>;
339	type AuthorizedAliasConsideration = Disabled;
340}
341
342impl cumulus_pallet_xcm::Config for Runtime {
343	type RuntimeEvent = RuntimeEvent;
344	type XcmExecutor = XcmExecutor;
345}
346
347impl cumulus_pallet_xcmp_queue::Config for Runtime {
348	type RuntimeEvent = RuntimeEvent;
349	type ChannelInfo = ParachainSystem;
350	type VersionWrapper = PolkadotXcm;
351	type XcmpQueue = TransformOrigin<MessageQueue, AggregateMessageOrigin, ParaId, ParaIdToSibling>;
352	type MaxInboundSuspended = sp_core::ConstU32<1_000>;
353	type ControllerOrigin = EnsureRoot<AccountId>;
354	type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin;
355	type WeightInfo = moonbeam_weights::cumulus_pallet_xcmp_queue::WeightInfo<Runtime>;
356	type PriceForSiblingDelivery = polkadot_runtime_common::xcm_sender::NoPriceForMessageDelivery<
357		cumulus_primitives_core::ParaId,
358	>;
359	type MaxActiveOutboundChannels = ConstU32<128>;
360	// Most on-chain HRMP channels are configured to use 102400 bytes of max message size, so we
361	// need to set the page size larger than that until we reduce the channel size on-chain.
362	type MaxPageSize = MessageQueueHeapSize;
363}
364
365parameter_types! {
366	pub const RelayOrigin: AggregateMessageOrigin = AggregateMessageOrigin::Parent;
367}
368
369parameter_types! {
370	/// The amount of weight (if any) which should be provided to the message queue for
371	/// servicing enqueued items.
372	///
373	/// This may be legitimately `None` in the case that you will call
374	/// `ServiceQueues::service_queues` manually.
375	pub MessageQueueServiceWeight: Weight =
376		Perbill::from_percent(25) * RuntimeBlockWeights::get().max_block;
377	/// The maximum number of stale pages (i.e. of overweight messages) allowed before culling
378	/// can happen. Once there are more stale pages than this, then historical pages may be
379	/// dropped, even if they contain unprocessed overweight messages.
380	pub const MessageQueueMaxStale: u32 = 8;
381	/// The size of the page; this implies the maximum message size which can be sent.
382	///
383	/// A good value depends on the expected message sizes, their weights, the weight that is
384	/// available for processing them and the maximal needed message size. The maximal message
385	/// size is slightly lower than this as defined by [`MaxMessageLenOf`].
386	pub const MessageQueueHeapSize: u32 = 103 * 1024;
387}
388
389impl pallet_message_queue::Config for Runtime {
390	type RuntimeEvent = RuntimeEvent;
391	#[cfg(feature = "runtime-benchmarks")]
392	type MessageProcessor = pallet_message_queue::mock_helpers::NoopMessageProcessor<
393		cumulus_primitives_core::AggregateMessageOrigin,
394	>;
395	#[cfg(not(feature = "runtime-benchmarks"))]
396	type MessageProcessor = pallet_ethereum_xcm::MessageProcessorWrapper<
397		xcm_builder::ProcessXcmMessage<AggregateMessageOrigin, XcmExecutor, RuntimeCall>,
398	>;
399	type Size = u32;
400	type HeapSize = MessageQueueHeapSize;
401	type MaxStale = MessageQueueMaxStale;
402	type ServiceWeight = MessageQueueServiceWeight;
403	// The XCMP queue pallet is only ever able to handle the `Sibling(ParaId)` origin:
404	type QueueChangeHandler = NarrowOriginToSibling<XcmpQueue>;
405	// NarrowOriginToSibling calls XcmpQueue's is_paused if Origin is sibling. Allows all other origins
406	type QueuePausedQuery = EmergencyParaXcm;
407	type WeightInfo = moonbeam_weights::pallet_message_queue::WeightInfo<Runtime>;
408	type IdleMaxServiceWeight = MessageQueueServiceWeight;
409}
410
411pub type FastAuthorizeUpgradeOrigin = EitherOfDiverse<
412	EnsureRoot<AccountId>,
413	pallet_collective::EnsureProportionAtLeast<AccountId, OpenTechCommitteeInstance, 5, 9>,
414>;
415
416pub type ResumeXcmOrigin = EitherOfDiverse<
417	EnsureRoot<AccountId>,
418	pallet_collective::EnsureProportionAtLeast<AccountId, OpenTechCommitteeInstance, 5, 9>,
419>;
420
421impl pallet_emergency_para_xcm::Config for Runtime {
422	type CheckAssociatedRelayNumber =
423		cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases;
424	type QueuePausedQuery = (MaintenanceMode, NarrowOriginToSibling<XcmpQueue>);
425	type PausedThreshold = ConstU32<300>;
426	type FastAuthorizeUpgradeOrigin = FastAuthorizeUpgradeOrigin;
427	type PausedToNormalOrigin = ResumeXcmOrigin;
428}
429
430// Our AssetType. For now we only handle Xcm Assets
431#[derive(
432	Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo, DecodeWithMemTracking,
433)]
434pub enum AssetType {
435	Xcm(xcm::v3::Location),
436}
437impl Default for AssetType {
438	fn default() -> Self {
439		Self::Xcm(xcm::v3::Location::here())
440	}
441}
442
443impl From<xcm::v3::Location> for AssetType {
444	fn from(location: xcm::v3::Location) -> Self {
445		Self::Xcm(location)
446	}
447}
448
449// This can be removed once we fully adopt xcm::v5 everywhere
450impl TryFrom<Location> for AssetType {
451	type Error = ();
452
453	fn try_from(location: Location) -> Result<Self, Self::Error> {
454		// Convert the V5 location to a V3 location
455		match xcm::VersionedLocation::V5(location).into_version(xcm::v3::VERSION) {
456			Ok(xcm::VersionedLocation::V3(loc)) => Ok(AssetType::Xcm(loc.into())),
457			// Any other version or conversion error returns an error
458			_ => Err(()),
459		}
460	}
461}
462
463impl Into<Option<xcm::v3::Location>> for AssetType {
464	fn into(self) -> Option<xcm::v3::Location> {
465		match self {
466			Self::Xcm(location) => Some(location),
467		}
468	}
469}
470
471impl Into<Option<Location>> for AssetType {
472	fn into(self) -> Option<Location> {
473		match self {
474			Self::Xcm(location) => {
475				let versioned = xcm::VersionedLocation::V3(location);
476				match versioned.into_version(xcm::latest::VERSION) {
477					Ok(xcm::VersionedLocation::V5(loc)) => Some(loc),
478					_ => None,
479				}
480			}
481		}
482	}
483}
484
485// Implementation on how to retrieve the AssetId from an AssetType
486// We simply hash the AssetType and take the lowest 128 bits
487impl From<AssetType> for AssetId {
488	fn from(asset: AssetType) -> AssetId {
489		match asset {
490			AssetType::Xcm(id) => {
491				let mut result: [u8; 16] = [0u8; 16];
492				let hash: H256 = id.using_encoded(<Runtime as frame_system::Config>::Hashing::hash);
493				result.copy_from_slice(&hash.as_fixed_bytes()[0..16]);
494				u128::from_le_bytes(result)
495			}
496		}
497	}
498}
499
500// Our currencyId. We distinguish for now between SelfReserve, and Others, defined by their Id.
501#[derive(
502	Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo, DecodeWithMemTracking,
503)]
504pub enum CurrencyId {
505	// Our native token
506	SelfReserve,
507	// Assets representing other chains native tokens
508	ForeignAsset(AssetId),
509	// Erc20 token
510	Erc20 { contract_address: H160 },
511}
512
513impl AccountIdToCurrencyId<AccountId, CurrencyId> for Runtime {
514	fn account_to_currency_id(account: AccountId) -> Option<CurrencyId> {
515		Some(match account {
516			// the self-reserve currency is identified by the pallet-balances address
517			a if a == H160::from_low_u64_be(2050).into() => CurrencyId::SelfReserve,
518			// the rest of the currencies, by their corresponding erc20 address
519			_ => match Runtime::account_to_asset_id(account) {
520				// We distinguish by prefix, and depending on it we create either
521				// Foreign or Local
522				Some((_prefix, asset_id)) => CurrencyId::ForeignAsset(asset_id),
523				// If no known prefix is identified, we consider that it's a "real" erc20 token
524				// (i.e. managed by a real smart contract)
525				None => CurrencyId::Erc20 {
526					contract_address: account.into(),
527				},
528			},
529		})
530	}
531}
532// How to convert from CurrencyId to Location
533pub struct CurrencyIdToLocation<AssetXConverter>(sp_std::marker::PhantomData<AssetXConverter>);
534impl<AssetXConverter> sp_runtime::traits::Convert<CurrencyId, Option<Location>>
535	for CurrencyIdToLocation<AssetXConverter>
536where
537	AssetXConverter: MaybeEquivalence<Location, AssetId>,
538{
539	fn convert(currency: CurrencyId) -> Option<Location> {
540		match currency {
541			CurrencyId::SelfReserve => {
542				let multi: Location = SelfReserve::get();
543				Some(multi)
544			}
545			CurrencyId::ForeignAsset(asset) => AssetXConverter::convert_back(&asset),
546			CurrencyId::Erc20 { contract_address } => {
547				let mut location = Erc20XcmBridgePalletLocation::get();
548				location
549					.push_interior(Junction::AccountKey20 {
550						key: contract_address.0,
551						network: None,
552					})
553					.ok();
554				Some(location)
555			}
556		}
557	}
558}
559
560parameter_types! {
561	pub const BaseXcmWeight: Weight = Weight::from_parts(200_000_000u64, 0);
562	pub const MaxAssetsForTransfer: usize = 2;
563
564	// This is how we are going to detect whether the asset is a Reserve asset
565	// This however is the chain part only
566	pub SelfLocation: Location = Location::here();
567	// We need this to be able to catch when someone is trying to execute a non-
568	// cross-chain transfer in xtokens through the absolute path way
569	pub SelfLocationAbsolute: Location = Location {
570		parents:1,
571		interior: [
572			Parachain(ParachainInfo::parachain_id().into())
573		].into()
574	};
575}
576
577// 1 DOT should be enough
578parameter_types! {
579	pub MaxHrmpRelayFee: Asset = (Location::parent(), 1_000_000_000_000u128).into();
580}
581
582// For now we only allow to transact in the relay, although this might change in the future
583// Transactors just defines the chains in which we allow transactions to be issued through
584// xcm
585#[derive(
586	Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo, DecodeWithMemTracking,
587)]
588pub enum Transactors {
589	Relay,
590	AssetHub,
591}
592
593// Default for benchmarking
594#[cfg(feature = "runtime-benchmarks")]
595impl Default for Transactors {
596	fn default() -> Self {
597		Transactors::Relay
598	}
599}
600
601impl TryFrom<u8> for Transactors {
602	type Error = ();
603	fn try_from(value: u8) -> Result<Self, Self::Error> {
604		match value {
605			0u8 => Ok(Transactors::Relay),
606			1u8 => Ok(Transactors::AssetHub),
607			_ => Err(()),
608		}
609	}
610}
611
612impl XcmTransact for Transactors {
613	fn destination(self) -> Location {
614		match self {
615			Transactors::Relay => RelayLocation::get(),
616			Transactors::AssetHub => AssetHubLocation::get(),
617		}
618	}
619
620	fn utility_pallet_index(&self) -> u8 {
621		match self {
622			Transactors::Relay => pallet_xcm_transactor::RelayIndices::<Runtime>::get().utility,
623			Transactors::AssetHub => pallet_xcm_transactor::ASSET_HUB_UTILITY_PALLET_INDEX,
624		}
625	}
626
627	fn staking_pallet_index(&self) -> u8 {
628		match self {
629			Transactors::Relay => pallet_xcm_transactor::RelayIndices::<Runtime>::get().staking,
630			Transactors::AssetHub => pallet_xcm_transactor::ASSET_HUB_STAKING_PALLET_INDEX,
631		}
632	}
633}
634
635pub type DerivativeAddressRegistrationOrigin =
636	EitherOfDiverse<EnsureRoot<AccountId>, governance::custom_origins::GeneralAdmin>;
637
638impl pallet_xcm_transactor::Config for Runtime {
639	type Balance = Balance;
640	type Transactor = Transactors;
641	type DerivativeAddressRegistrationOrigin = DerivativeAddressRegistrationOrigin;
642	type SovereignAccountDispatcherOrigin = EnsureRoot<AccountId>;
643	type CurrencyId = CurrencyId;
644	type AccountIdToLocation = AccountIdToLocation<AccountId>;
645	type CurrencyIdToLocation = CurrencyIdToLocation<(EvmForeignAssets,)>;
646	type XcmSender = XcmRouter;
647	type SelfLocation = SelfLocation;
648	type Weigher = XcmWeigher;
649	type UniversalLocation = UniversalLocation;
650	type BaseXcmWeight = BaseXcmWeight;
651	type AssetTransactor = AssetTransactors;
652	type ReserveProvider = AbsoluteAndRelativeReserve<SelfLocationAbsolute>;
653	type WeightInfo = moonbeam_weights::pallet_xcm_transactor::WeightInfo<Runtime>;
654	type HrmpManipulatorOrigin = GeneralAdminOrRoot;
655	type HrmpOpenOrigin = FastGeneralAdminOrRoot;
656	type MaxHrmpFee = xcm_builder::Case<MaxHrmpRelayFee>;
657}
658
659parameter_types! {
660	// This is the relative view of erc20 assets.
661	// Identified by this prefix + AccountKey20(contractAddress)
662	// We use the RELATIVE multilocation
663	pub Erc20XcmBridgePalletLocation: Location = Location {
664		parents:0,
665		interior: [
666			PalletInstance(<Erc20XcmBridge as PalletInfoAccess>::index() as u8)
667		].into()
668	};
669
670	// To be able to support almost all erc20 implementations,
671	// we provide a sufficiently hight gas limit.
672	pub Erc20XcmBridgeTransferGasLimit: u64 = 400_000;
673}
674
675impl pallet_erc20_xcm_bridge::Config for Runtime {
676	type AccountIdConverter = LocationToH160;
677	type Erc20MultilocationPrefix = Erc20XcmBridgePalletLocation;
678	type Erc20TransferGasLimit = Erc20XcmBridgeTransferGasLimit;
679	type EvmRunner = EvmRunnerPrecompileOrEthXcm<MoonbeamCall, Self>;
680}
681
682pub struct AccountIdToH160;
683impl sp_runtime::traits::Convert<AccountId, H160> for AccountIdToH160 {
684	fn convert(account_id: AccountId) -> H160 {
685		account_id.into()
686	}
687}
688
689pub type ForeignAssetManagerOrigin = EitherOf<
690	MapSuccessToXcm<EnsureXcm<AllowSiblingParachains>>,
691	MapSuccessToGovernance<
692		EitherOf<
693			EnsureRoot<AccountId>,
694			EitherOf<
695				pallet_collective::EnsureProportionMoreThan<
696					AccountId,
697					OpenTechCommitteeInstance,
698					5,
699					9,
700				>,
701				EitherOf<
702					governance::custom_origins::FastGeneralAdmin,
703					governance::custom_origins::GeneralAdmin,
704				>,
705			>,
706		>,
707	>,
708>;
709
710impl pallet_moonbeam_foreign_assets::Config for Runtime {
711	type AccountIdToH160 = AccountIdToH160;
712	type AssetIdFilter = Everything;
713	type EvmRunner = EvmRunnerPrecompileOrEthXcm<MoonbeamCall, Self>;
714	type ConvertLocation =
715		SiblingParachainConvertsVia<polkadot_parachain::primitives::Sibling, AccountId>;
716	type ForeignAssetCreatorOrigin = ForeignAssetManagerOrigin;
717	type ForeignAssetFreezerOrigin = ForeignAssetManagerOrigin;
718	type ForeignAssetModifierOrigin = ForeignAssetManagerOrigin;
719	type ForeignAssetUnfreezerOrigin = ForeignAssetManagerOrigin;
720	type OnForeignAssetCreated = ();
721	type MaxForeignAssets = ConstU32<256>;
722	type WeightInfo = moonbeam_weights::pallet_moonbeam_foreign_assets::WeightInfo<Runtime>;
723	type XcmLocationToH160 = LocationToH160;
724	type ForeignAssetCreationDeposit = dynamic_params::xcm_config::ForeignAssetCreationDeposit;
725	type Balance = Balance;
726	type Currency = Balances;
727}
728
729pub struct AssetFeesFilter;
730impl frame_support::traits::Contains<Location> for AssetFeesFilter {
731	fn contains(location: &Location) -> bool {
732		location.parent_count() > 0
733			&& location.first_interior() != Erc20XcmBridgePalletLocation::get().first_interior()
734	}
735}
736
737pub type AddAndEditSupportedAssetOrigin = EitherOfDiverse<
738	EnsureRoot<AccountId>,
739	EitherOfDiverse<
740		pallet_collective::EnsureProportionMoreThan<AccountId, OpenTechCommitteeInstance, 5, 9>,
741		EitherOf<
742			governance::custom_origins::GeneralAdmin,
743			governance::custom_origins::FastGeneralAdmin,
744		>,
745	>,
746>;
747
748pub type RemoveSupportedAssetOrigin = EitherOfDiverse<
749	EnsureRoot<AccountId>,
750	pallet_collective::EnsureProportionMoreThan<AccountId, OpenTechCommitteeInstance, 5, 9>,
751>;
752
753impl pallet_xcm_weight_trader::Config for Runtime {
754	type AccountIdToLocation = AccountIdToLocation<AccountId>;
755	type AddSupportedAssetOrigin = AddAndEditSupportedAssetOrigin;
756	type AssetLocationFilter = AssetFeesFilter;
757	type AssetTransactor = AssetTransactors;
758	type Balance = Balance;
759	type EditSupportedAssetOrigin = AddAndEditSupportedAssetOrigin;
760	type NativeLocation = SelfReserve;
761	type PauseSupportedAssetOrigin = AddAndEditSupportedAssetOrigin;
762	type ResumeSupportedAssetOrigin = AddAndEditSupportedAssetOrigin;
763	type RemoveSupportedAssetOrigin = RemoveSupportedAssetOrigin;
764	type WeightInfo = moonbeam_weights::pallet_xcm_weight_trader::WeightInfo<Runtime>;
765	type WeightToFee = <Runtime as pallet_transaction_payment::Config>::WeightToFee;
766	type XcmFeesAccount = XcmFeesAccount;
767	#[cfg(feature = "runtime-benchmarks")]
768	type NotFilteredLocation = RelayLocation;
769}
770
771#[cfg(feature = "runtime-benchmarks")]
772mod testing {
773	use super::*;
774
775	/// This From exists for benchmarking purposes. It has the potential side-effect of calling
776	/// EvmForeignAssets::set_asset() and should NOT be used in any production code.
777	impl From<Location> for CurrencyId {
778		fn from(location: Location) -> CurrencyId {
779			use sp_runtime::traits::MaybeEquivalence;
780
781			// If it does not exist, for benchmarking purposes, we create the association
782			let asset_id = if let Some(asset_id) = EvmForeignAssets::convert(&location) {
783				asset_id
784			} else {
785				// Generate asset ID from location hash (similar to old AssetManager approach)
786				let hash: H256 =
787					location.using_encoded(<Runtime as frame_system::Config>::Hashing::hash);
788				let mut result: [u8; 16] = [0u8; 16];
789				result.copy_from_slice(&hash.as_fixed_bytes()[0..16]);
790				let asset_id = u128::from_le_bytes(result);
791
792				EvmForeignAssets::set_asset(location, asset_id);
793				asset_id
794			};
795
796			CurrencyId::ForeignAsset(asset_id)
797		}
798	}
799}