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