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