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

            
20
use super::{
21
	bridge_config, governance, AccountId, AssetId, AssetManager, Balance, Balances,
22
	BridgeXcmOverMoonriver, EmergencyParaXcm, Erc20XcmBridge, EvmForeignAssets, MaintenanceMode,
23
	MessageQueue, OpenTechCommitteeInstance, ParachainInfo, ParachainSystem, Perbill, PolkadotXcm,
24
	Runtime, RuntimeBlockWeights, RuntimeCall, RuntimeEvent, RuntimeOrigin, Treasury, XcmpQueue,
25
};
26

            
27
use super::moonbeam_weights;
28
use frame_support::{
29
	parameter_types,
30
	traits::{EitherOf, EitherOfDiverse, Everything, Nothing, PalletInfoAccess, TransformOrigin},
31
};
32
use moonkit_xcm_primitives::AccountIdAssetIdConversion;
33
use sp_runtime::{
34
	traits::{Hash as THash, MaybeEquivalence, PostDispatchInfoOf},
35
	DispatchErrorWithPostInfo,
36
};
37
use sp_weights::Weight;
38

            
39
use frame_system::{EnsureRoot, RawOrigin};
40
use sp_core::{ConstU32, H160, H256};
41

            
42
use xcm_builder::{
43
	AccountKey20Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom,
44
	AllowTopLevelPaidExecutionFrom, Case, ConvertedConcreteId, DescribeAllTerminal, DescribeFamily,
45
	EnsureXcmOrigin, FungibleAdapter as XcmCurrencyAdapter, FungiblesAdapter,
46
	GlobalConsensusParachainConvertsFor, HashedDescription, NoChecking, ParentIsPreset,
47
	RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia,
48
	SignedAccountKey20AsNative, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId,
49
	WeightInfoBounds, WithComputedOrigin, WithUniqueTopic,
50
};
51

            
52
use parachains_common::message_queue::{NarrowOriginToSibling, ParaIdToSibling};
53
use xcm::{
54
	latest::prelude::{
55
		AllOf, Asset, AssetFilter, GlobalConsensus, InteriorLocation, Junction, Location,
56
		NetworkId, PalletInstance, Parachain, Wild, WildFungible,
57
	},
58
	IntoVersion,
59
};
60

            
61
use xcm_executor::traits::{CallDispatcher, ConvertLocation, JustTry};
62

            
63
use cumulus_primitives_core::{AggregateMessageOrigin, ParaId};
64
use pallet_xcm::EnsureXcm;
65
use xcm_primitives::{
66
	AbsoluteAndRelativeReserve, AccountIdToCurrencyId, AccountIdToLocation, AsAssetType,
67
	IsBridgedConcreteAssetFrom, MultiNativeAsset, SignedToAccountId20, UtilityAvailableCalls,
68
	UtilityEncodeCall, XcmTransact,
69
};
70

            
71
use crate::governance::referenda::{FastGeneralAdminOrRoot, GeneralAdminOrRoot};
72
use crate::runtime_params::dynamic_params;
73
use moonbeam_runtime_common::xcm_origins::AllowSiblingParachains;
74
use pallet_moonbeam_foreign_assets::{MapSuccessToGovernance, MapSuccessToXcm};
75
use parity_scale_codec::{Decode, Encode};
76
use scale_info::TypeInfo;
77
use sp_core::Get;
78
use sp_std::{
79
	convert::{From, Into, TryFrom},
80
	prelude::*,
81
};
82

            
83
parameter_types! {
84
	// The network Id of the relay
85
	pub const RelayNetwork: NetworkId = NetworkId::Polkadot;
86
	// The relay chain Origin type
87
	pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into();
88
	pub UniversalLocation: InteriorLocation =
89
		[GlobalConsensus(RelayNetwork::get()), Parachain(ParachainInfo::parachain_id().into())].into();
90
	// Self Reserve location, defines the multilocation identifiying the self-reserve currency
91
	// This is used to match it also against our Balances pallet when we receive such
92
	// a Location: (Self Balances pallet index)
93
	// We use the RELATIVE multilocation
94
	pub SelfReserve: Location = Location {
95
		parents:0,
96
		interior: [
97
			PalletInstance(<Balances as PalletInfoAccess>::index() as u8)
98
		].into()
99
	};
100
}
101

            
102
/// Type for specifying how a `Location` can be converted into an `AccountId`. This is used
103
/// when determining ownership of accounts for asset transacting and when attempting to use XCM
104
/// `Transact` in order to determine the dispatch Origin.
105
pub type LocationToAccountId = (
106
	// The parent (Relay-chain) origin converts to the default `AccountId`.
107
	ParentIsPreset<AccountId>,
108
	// Sibling parachain origins convert to AccountId via the `ParaId::into`.
109
	SiblingParachainConvertsVia<polkadot_parachain::primitives::Sibling, AccountId>,
110
	// If we receive a Location of type AccountKey20, just generate a native account
111
	AccountKey20Aliases<RelayNetwork, AccountId>,
112
	// Foreign locations alias into accounts according to a hash of their standard description.
113
	HashedDescription<AccountId, DescribeFamily<DescribeAllTerminal>>,
114
	// Different global consensus parachain sovereign account.
115
	// (Used for over-bridge transfers and reserve processing)
116
	GlobalConsensusParachainConvertsFor<UniversalLocation, AccountId>,
117
);
118

            
119
/// Wrapper type around `LocationToAccountId` to convert an `AccountId` to type `H160`.
120
pub struct LocationToH160;
121
impl ConvertLocation<H160> for LocationToH160 {
122
18
	fn convert_location(location: &Location) -> Option<H160> {
123
18
		<LocationToAccountId as ConvertLocation<AccountId>>::convert_location(location)
124
18
			.map(Into::into)
125
18
	}
126
}
127

            
128
// The non-reserve fungible transactor type
129
// It will use pallet-assets, and the Id will be matched against AsAssetType
130
pub type ForeignFungiblesTransactor = FungiblesAdapter<
131
	// Use this fungibles implementation:
132
	super::Assets,
133
	// Use this currency when it is a fungible asset matching the given location or name:
134
	(
135
		ConvertedConcreteId<
136
			AssetId,
137
			Balance,
138
			AsAssetType<AssetId, AssetType, AssetManager>,
139
			JustTry,
140
		>,
141
	),
142
	// Do a simple punn to convert an AccountId20 Location into a native chain account ID:
143
	LocationToAccountId,
144
	// Our chain's account ID type (we can't get away without mentioning it explicitly):
145
	AccountId,
146
	// We dont allow teleports.
147
	NoChecking,
148
	// We dont track any teleports
149
	(),
150
>;
151

            
152
/// The transactor for our own chain currency.
153
pub type LocalAssetTransactor = XcmCurrencyAdapter<
154
	// Use this currency:
155
	Balances,
156
	// Use this currency when it is a fungible asset matching any of the locations in
157
	// SelfReserveRepresentations
158
	xcm_builder::IsConcrete<SelfReserve>,
159
	// We can convert the MultiLocations with our converter above:
160
	LocationToAccountId,
161
	// Our chain's account ID type (we can't get away without mentioning it explicitly):
162
	AccountId,
163
	// We dont allow teleport
164
	(),
165
>;
166

            
167
// We use all transactors
168
pub type AssetTransactors = (
169
	LocalAssetTransactor,
170
	EvmForeignAssets,
171
	ForeignFungiblesTransactor,
172
	Erc20XcmBridge,
173
);
174

            
175
/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance,
176
/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can
177
/// biases the kind of local `Origin` it will become.
178
pub type XcmOriginToTransactDispatchOrigin = (
179
	// Sovereign account converter; this attempts to derive an `AccountId` from the origin location
180
	// using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for
181
	// foreign chains who want to have a local sovereign account on this chain which they control.
182
	SovereignSignedViaLocation<LocationToAccountId, RuntimeOrigin>,
183
	// Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when
184
	// recognised.
185
	RelayChainAsNative<RelayChainOrigin, RuntimeOrigin>,
186
	// Native converter for sibling Parachains; will convert to a `SiblingPara` origin when
187
	// recognised.
188
	SiblingParachainAsNative<cumulus_pallet_xcm::Origin, RuntimeOrigin>,
189
	// Xcm origins can be represented natively under the Xcm pallet's Xcm origin.
190
	pallet_xcm::XcmPassthrough<RuntimeOrigin>,
191
	// Xcm Origins defined by a Multilocation of type AccountKey20 can be converted to a 20 byte-
192
	// account local origin
193
	SignedAccountKey20AsNative<RelayNetwork, RuntimeOrigin>,
194
);
195

            
196
parameter_types! {
197
	/// Maximum number of instructions in a single XCM fragment. A sanity check against
198
	/// weight caculations getting too crazy.
199
	pub MaxInstructions: u32 = 100;
200
}
201

            
202
/// Xcm Weigher shared between multiple Xcm-related configs.
203
pub type XcmWeigher = WeightInfoBounds<
204
	crate::weights::xcm::XcmWeight<Runtime, RuntimeCall>,
205
	RuntimeCall,
206
	MaxInstructions,
207
>;
208

            
209
pub type XcmBarrier = TrailingSetTopicAsId<(
210
	// Weight that is paid for may be consumed.
211
	TakeWeightCredit,
212
	// Expected responses are OK.
213
	AllowKnownQueryResponses<PolkadotXcm>,
214
	WithComputedOrigin<
215
		(
216
			// If the message is one that immediately attemps to pay for execution, then allow it.
217
			AllowTopLevelPaidExecutionFrom<Everything>,
218
			// Subscriptions for version tracking are OK.
219
			AllowSubscriptionsFrom<Everything>,
220
		),
221
		UniversalLocation,
222
		ConstU32<8>,
223
	>,
224
)>;
225

            
226
parameter_types! {
227
	/// Xcm fees will go to the treasury account
228
	pub XcmFeesAccount: AccountId = Treasury::account_id();
229
}
230

            
231
pub struct SafeCallFilter;
232
impl frame_support::traits::Contains<RuntimeCall> for SafeCallFilter {
233
	fn contains(_call: &RuntimeCall) -> bool {
234
		// TODO review
235
		// This needs to be addressed at EVM level
236
		true
237
	}
238
}
239

            
240
parameter_types! {
241
	 /// Location of Asset Hub
242
	pub AssetHubLocation: Location = Location::new(1, [Parachain(1000)]);
243
	pub const RelayLocation: Location = Location::parent();
244
	pub RelayLocationFilter: AssetFilter = Wild(AllOf {
245
		fun: WildFungible,
246
		id: xcm::prelude::AssetId(RelayLocation::get()),
247
	});
248
	pub RelayChainNativeAssetFromAssetHub: (AssetFilter, Location) = (
249
		RelayLocationFilter::get(),
250
		AssetHubLocation::get()
251
	);
252
	pub const MaxAssetsIntoHolding: u32 = xcm_primitives::MAX_ASSETS;
253
}
254

            
255
type Reserves = (
256
	// Assets bridged from different consensus systems held in reserve on Asset Hub.
257
	IsBridgedConcreteAssetFrom<AssetHubLocation>,
258
	// Assets bridged from Moonriver
259
	IsBridgedConcreteAssetFrom<bp_moonriver::GlobalConsensusLocation>,
260
	// Relaychain (DOT) from Asset Hub
261
	Case<RelayChainNativeAssetFromAssetHub>,
262
	// Assets which the reserve is the same as the origin.
263
	MultiNativeAsset<AbsoluteAndRelativeReserve<SelfLocationAbsolute>>,
264
);
265

            
266
// Our implementation of the Moonbeam Call
267
// Attaches the right origin in case the call is made to pallet-ethereum-xcm
268
#[cfg(not(feature = "evm-tracing"))]
269
moonbeam_runtime_common::impl_moonbeam_xcm_call!();
270
#[cfg(feature = "evm-tracing")]
271
moonbeam_runtime_common::impl_moonbeam_xcm_call_tracing!();
272

            
273
moonbeam_runtime_common::impl_evm_runner_precompile_or_eth_xcm!();
274

            
275
pub struct XcmExecutorConfig;
276
impl xcm_executor::Config for XcmExecutorConfig {
277
	type RuntimeCall = RuntimeCall;
278
	type XcmSender = XcmRouter;
279
	// How to withdraw and deposit an asset.
280
	type AssetTransactor = AssetTransactors;
281
	type OriginConverter = XcmOriginToTransactDispatchOrigin;
282
	// Filter to the reserve withdraw operations
283
	// Whenever the reserve matches the relative or absolute value
284
	// of our chain, we always return the relative reserve
285
	type IsReserve = Reserves;
286
	type IsTeleporter = (); // No teleport
287
	type UniversalLocation = UniversalLocation;
288
	type Barrier = XcmBarrier;
289
	type Weigher = XcmWeigher;
290
	// As trader we use the XcmWeightTrader pallet.
291
	// For each foreign asset, the fee is computed based on its relative price (also
292
	// stored in the XcmWeightTrader pallet) against the native asset.
293
	// For the native asset fee is computed using WeightToFee implementation.
294
	type Trader = pallet_xcm_weight_trader::Trader<Runtime>;
295
	type ResponseHandler = PolkadotXcm;
296
	type SubscriptionService = PolkadotXcm;
297
	type AssetTrap = pallet_erc20_xcm_bridge::AssetTrapWrapper<PolkadotXcm, Runtime>;
298
	type AssetClaims = PolkadotXcm;
299
	type CallDispatcher = MoonbeamCall;
300
	type PalletInstancesInfo = crate::AllPalletsWithSystem;
301
	type MaxAssetsIntoHolding = MaxAssetsIntoHolding;
302
	type AssetLocker = ();
303
	type AssetExchanger = ();
304
	type FeeManager = ();
305
	type MessageExporter = BridgeXcmOverMoonriver;
306
	type UniversalAliases = bridge_config::UniversalAliases;
307
	type SafeCallFilter = SafeCallFilter;
308
	type Aliasers = Nothing;
309
	type TransactionalProcessor = xcm_builder::FrameTransactionalProcessor;
310
	type HrmpNewChannelOpenRequestHandler = ();
311
	type HrmpChannelAcceptedHandler = ();
312
	type HrmpChannelClosingHandler = ();
313
	type XcmRecorder = PolkadotXcm;
314
}
315

            
316
pub type XcmExecutor = pallet_erc20_xcm_bridge::XcmExecutorWrapper<
317
	XcmExecutorConfig,
318
	xcm_executor::XcmExecutor<XcmExecutorConfig>,
319
>;
320

            
321
// Converts a Signed Local Origin into a Location
322
pub type LocalOriginToLocation = SignedToAccountId20<RuntimeOrigin, AccountId, RelayNetwork>;
323

            
324
/// For routing XCM messages which do not cross local consensus boundary.
325
pub type LocalXcmRouter = (
326
	// Two routers - use UMP to communicate with the relay chain:
327
	cumulus_primitives_utility::ParentAsUmp<ParachainSystem, PolkadotXcm, ()>,
328
	// ..and XCMP to communicate with the sibling chains.
329
	XcmpQueue,
330
);
331

            
332
/// The means for routing XCM messages which are not for local execution into the right message
333
/// queues.
334
pub type XcmRouter = WithUniqueTopic<(
335
	// The means for routing XCM messages which are not for local execution into the right message
336
	// queues.
337
	LocalXcmRouter,
338
	// Router that exports messages to be delivered to the Kusama GlobalConsensus
339
	moonbeam_runtime_common::bridge::BridgeXcmRouter<
340
		xcm_builder::LocalExporter<BridgeXcmOverMoonriver, UniversalLocation>,
341
	>,
342
)>;
343

            
344
impl pallet_xcm::Config for Runtime {
345
	type RuntimeEvent = RuntimeEvent;
346
	type SendXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
347
	type XcmRouter = XcmRouter;
348
	type ExecuteXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
349
	type XcmExecuteFilter = Nothing;
350
	type XcmExecutor = XcmExecutor;
351
	type XcmTeleportFilter = Nothing;
352
	type XcmReserveTransferFilter = Everything;
353
	type Weigher = XcmWeigher;
354
	type UniversalLocation = UniversalLocation;
355
	type RuntimeOrigin = RuntimeOrigin;
356
	type RuntimeCall = RuntimeCall;
357
	const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
358
	type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
359
	type Currency = Balances;
360
	type CurrencyMatcher = ();
361
	type TrustedLockers = ();
362
	type SovereignAccountOf = LocationToAccountId;
363
	type MaxLockers = ConstU32<8>;
364
	type MaxRemoteLockConsumers = ConstU32<0>;
365
	type RemoteLockConsumerIdentifier = ();
366
	type WeightInfo = moonbeam_weights::pallet_xcm::WeightInfo<Runtime>;
367
	type AdminOrigin = EnsureRoot<AccountId>;
368
}
369

            
370
impl cumulus_pallet_xcm::Config for Runtime {
371
	type RuntimeEvent = RuntimeEvent;
372
	type XcmExecutor = XcmExecutor;
373
}
374

            
375
impl cumulus_pallet_xcmp_queue::Config for Runtime {
376
	type RuntimeEvent = RuntimeEvent;
377
	type ChannelInfo = ParachainSystem;
378
	type VersionWrapper = PolkadotXcm;
379
	type XcmpQueue = TransformOrigin<MessageQueue, AggregateMessageOrigin, ParaId, ParaIdToSibling>;
380
	type MaxInboundSuspended = sp_core::ConstU32<1_000>;
381
	type ControllerOrigin = EnsureRoot<AccountId>;
382
	type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin;
383
	type WeightInfo = moonbeam_weights::cumulus_pallet_xcmp_queue::WeightInfo<Runtime>;
384
	type PriceForSiblingDelivery = polkadot_runtime_common::xcm_sender::NoPriceForMessageDelivery<
385
		cumulus_primitives_core::ParaId,
386
	>;
387
	type MaxActiveOutboundChannels = ConstU32<128>;
388
	// Most on-chain HRMP channels are configured to use 102400 bytes of max message size, so we
389
	// need to set the page size larger than that until we reduce the channel size on-chain.
390
	type MaxPageSize = MessageQueueHeapSize;
391
}
392

            
393
parameter_types! {
394
	pub const RelayOrigin: AggregateMessageOrigin = AggregateMessageOrigin::Parent;
395
}
396

            
397
parameter_types! {
398
	/// The amount of weight (if any) which should be provided to the message queue for
399
	/// servicing enqueued items.
400
	///
401
	/// This may be legitimately `None` in the case that you will call
402
	/// `ServiceQueues::service_queues` manually.
403
	pub MessageQueueServiceWeight: Weight =
404
		Perbill::from_percent(25) * RuntimeBlockWeights::get().max_block;
405
	/// The maximum number of stale pages (i.e. of overweight messages) allowed before culling
406
	/// can happen. Once there are more stale pages than this, then historical pages may be
407
	/// dropped, even if they contain unprocessed overweight messages.
408
	pub const MessageQueueMaxStale: u32 = 8;
409
	/// The size of the page; this implies the maximum message size which can be sent.
410
	///
411
	/// A good value depends on the expected message sizes, their weights, the weight that is
412
	/// available for processing them and the maximal needed message size. The maximal message
413
	/// size is slightly lower than this as defined by [`MaxMessageLenOf`].
414
	pub const MessageQueueHeapSize: u32 = 103 * 1024;
415
}
416

            
417
impl pallet_message_queue::Config for Runtime {
418
	type RuntimeEvent = RuntimeEvent;
419
	#[cfg(feature = "runtime-benchmarks")]
420
	type MessageProcessor = pallet_message_queue::mock_helpers::NoopMessageProcessor<
421
		cumulus_primitives_core::AggregateMessageOrigin,
422
	>;
423
	#[cfg(not(feature = "runtime-benchmarks"))]
424
	type MessageProcessor = pallet_ethereum_xcm::MessageProcessorWrapper<
425
		xcm_builder::ProcessXcmMessage<AggregateMessageOrigin, XcmExecutor, RuntimeCall>,
426
	>;
427
	type Size = u32;
428
	type HeapSize = MessageQueueHeapSize;
429
	type MaxStale = MessageQueueMaxStale;
430
	type ServiceWeight = MessageQueueServiceWeight;
431
	// The XCMP queue pallet is only ever able to handle the `Sibling(ParaId)` origin:
432
	type QueueChangeHandler = NarrowOriginToSibling<XcmpQueue>;
433
	// NarrowOriginToSibling calls XcmpQueue's is_paused if Origin is sibling. Allows all other origins
434
	type QueuePausedQuery = EmergencyParaXcm;
435
	type WeightInfo = moonbeam_weights::pallet_message_queue::WeightInfo<Runtime>;
436
	type IdleMaxServiceWeight = MessageQueueServiceWeight;
437
}
438

            
439
pub type FastAuthorizeUpgradeOrigin = EitherOfDiverse<
440
	EnsureRoot<AccountId>,
441
	pallet_collective::EnsureProportionAtLeast<AccountId, OpenTechCommitteeInstance, 5, 9>,
442
>;
443

            
444
pub type ResumeXcmOrigin = EitherOfDiverse<
445
	EnsureRoot<AccountId>,
446
	pallet_collective::EnsureProportionAtLeast<AccountId, OpenTechCommitteeInstance, 5, 9>,
447
>;
448

            
449
impl pallet_emergency_para_xcm::Config for Runtime {
450
	type RuntimeEvent = RuntimeEvent;
451
	type CheckAssociatedRelayNumber =
452
		cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases;
453
	type QueuePausedQuery = (MaintenanceMode, NarrowOriginToSibling<XcmpQueue>);
454
	type PausedThreshold = ConstU32<300>;
455
	type FastAuthorizeUpgradeOrigin = FastAuthorizeUpgradeOrigin;
456
	type PausedToNormalOrigin = ResumeXcmOrigin;
457
}
458

            
459
// Our AssetType. For now we only handle Xcm Assets
460
12
#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
461
pub enum AssetType {
462
26
	Xcm(xcm::v3::Location),
463
}
464
impl Default for AssetType {
465
	fn default() -> Self {
466
		Self::Xcm(xcm::v3::Location::here())
467
	}
468
}
469

            
470
impl From<xcm::v3::Location> for AssetType {
471
420
	fn from(location: xcm::v3::Location) -> Self {
472
420
		Self::Xcm(location)
473
420
	}
474
}
475

            
476
// This can be removed once we fully adopt xcm::v5 everywhere
477
impl TryFrom<Location> for AssetType {
478
	type Error = ();
479

            
480
54
	fn try_from(location: Location) -> Result<Self, Self::Error> {
481
54
		// Convert the V5 location to a V3 location
482
54
		match xcm::VersionedLocation::V5(location).into_version(xcm::v3::VERSION) {
483
54
			Ok(xcm::VersionedLocation::V3(loc)) => Ok(AssetType::Xcm(loc.into())),
484
			// Any other version or conversion error returns an error
485
			_ => Err(()),
486
		}
487
54
	}
488
}
489

            
490
impl Into<Option<xcm::v3::Location>> for AssetType {
491
132
	fn into(self) -> Option<xcm::v3::Location> {
492
132
		match self {
493
132
			Self::Xcm(location) => Some(location),
494
132
		}
495
132
	}
496
}
497

            
498
impl Into<Option<Location>> for AssetType {
499
	fn into(self) -> Option<Location> {
500
		match self {
501
			Self::Xcm(location) => {
502
				let versioned = xcm::VersionedLocation::V3(location);
503
				match versioned.into_version(xcm::latest::VERSION) {
504
					Ok(xcm::VersionedLocation::V5(loc)) => Some(loc),
505
					_ => None,
506
				}
507
			}
508
		}
509
	}
510
}
511

            
512
// Implementation on how to retrieve the AssetId from an AssetType
513
// We simply hash the AssetType and take the lowest 128 bits
514
impl From<AssetType> for AssetId {
515
546
	fn from(asset: AssetType) -> AssetId {
516
546
		match asset {
517
546
			AssetType::Xcm(id) => {
518
546
				let mut result: [u8; 16] = [0u8; 16];
519
546
				let hash: H256 = id.using_encoded(<Runtime as frame_system::Config>::Hashing::hash);
520
546
				result.copy_from_slice(&hash.as_fixed_bytes()[0..16]);
521
546
				u128::from_le_bytes(result)
522
546
			}
523
546
		}
524
546
	}
525
}
526

            
527
// Our currencyId. We distinguish for now between SelfReserve, and Others, defined by their Id.
528
18
#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
529
pub enum CurrencyId {
530
	// Our native token
531
	SelfReserve,
532
	// Assets representing other chains native tokens
533
	ForeignAsset(AssetId),
534
	// Erc20 token
535
	Erc20 { contract_address: H160 },
536
}
537

            
538
impl AccountIdToCurrencyId<AccountId, CurrencyId> for Runtime {
539
12
	fn account_to_currency_id(account: AccountId) -> Option<CurrencyId> {
540
12
		Some(match account {
541
			// the self-reserve currency is identified by the pallet-balances address
542
12
			a if a == H160::from_low_u64_be(2050).into() => CurrencyId::SelfReserve,
543
			// the rest of the currencies, by their corresponding erc20 address
544
12
			_ => match Runtime::account_to_asset_id(account) {
545
				// We distinguish by prefix, and depending on it we create either
546
				// Foreign or Local
547
12
				Some((_prefix, asset_id)) => CurrencyId::ForeignAsset(asset_id),
548
				// If no known prefix is identified, we consider that it's a "real" erc20 token
549
				// (i.e. managed by a real smart contract)
550
				None => CurrencyId::Erc20 {
551
					contract_address: account.into(),
552
				},
553
			},
554
		})
555
12
	}
556
}
557
// How to convert from CurrencyId to Location
558
pub struct CurrencyIdToLocation<AssetXConverter>(sp_std::marker::PhantomData<AssetXConverter>);
559
impl<AssetXConverter> sp_runtime::traits::Convert<CurrencyId, Option<Location>>
560
	for CurrencyIdToLocation<AssetXConverter>
561
where
562
	AssetXConverter: MaybeEquivalence<Location, AssetId>,
563
{
564
7
	fn convert(currency: CurrencyId) -> Option<Location> {
565
7
		match currency {
566
			CurrencyId::SelfReserve => {
567
1
				let multi: Location = SelfReserve::get();
568
1
				Some(multi)
569
			}
570
6
			CurrencyId::ForeignAsset(asset) => AssetXConverter::convert_back(&asset),
571
			CurrencyId::Erc20 { contract_address } => {
572
				let mut location = Erc20XcmBridgePalletLocation::get();
573
				location
574
					.push_interior(Junction::AccountKey20 {
575
						key: contract_address.0,
576
						network: None,
577
					})
578
					.ok();
579
				Some(location)
580
			}
581
		}
582
7
	}
583
}
584

            
585
parameter_types! {
586
	pub const BaseXcmWeight: Weight = Weight::from_parts(200_000_000u64, 0);
587
	pub const MaxAssetsForTransfer: usize = 2;
588

            
589
	// This is how we are going to detect whether the asset is a Reserve asset
590
	// This however is the chain part only
591
	pub SelfLocation: Location = Location::here();
592
	// We need this to be able to catch when someone is trying to execute a non-
593
	// cross-chain transfer in xtokens through the absolute path way
594
	pub SelfLocationAbsolute: Location = Location {
595
		parents:1,
596
		interior: [
597
			Parachain(ParachainInfo::parachain_id().into())
598
		].into()
599
	};
600
}
601

            
602
// 1 DOT should be enough
603
parameter_types! {
604
	pub MaxHrmpRelayFee: Asset = (Location::parent(), 1_000_000_000_000u128).into();
605
}
606

            
607
// For now we only allow to transact in the relay, although this might change in the future
608
// Transactors just defines the chains in which we allow transactions to be issued through
609
// xcm
610
6
#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
611
pub enum Transactors {
612
	Relay,
613
}
614

            
615
// Default for benchmarking
616
#[cfg(feature = "runtime-benchmarks")]
617
impl Default for Transactors {
618
	fn default() -> Self {
619
		Transactors::Relay
620
	}
621
}
622

            
623
impl TryFrom<u8> for Transactors {
624
	type Error = ();
625
	fn try_from(value: u8) -> Result<Self, Self::Error> {
626
		match value {
627
			0u8 => Ok(Transactors::Relay),
628
			_ => Err(()),
629
		}
630
	}
631
}
632

            
633
impl UtilityEncodeCall for Transactors {
634
12
	fn encode_call(self, call: UtilityAvailableCalls) -> Vec<u8> {
635
12
		match self {
636
12
			Transactors::Relay => pallet_xcm_transactor::Pallet::<Runtime>::encode_call(
637
12
				pallet_xcm_transactor::Pallet(sp_std::marker::PhantomData::<Runtime>),
638
12
				call,
639
12
			),
640
12
		}
641
12
	}
642
}
643

            
644
impl XcmTransact for Transactors {
645
12
	fn destination(self) -> Location {
646
12
		match self {
647
12
			Transactors::Relay => Location::parent(),
648
12
		}
649
12
	}
650
}
651

            
652
pub type DerivativeAddressRegistrationOrigin =
653
	EitherOfDiverse<EnsureRoot<AccountId>, governance::custom_origins::GeneralAdmin>;
654

            
655
impl pallet_xcm_transactor::Config for Runtime {
656
	type RuntimeEvent = RuntimeEvent;
657
	type Balance = Balance;
658
	type Transactor = Transactors;
659
	type DerivativeAddressRegistrationOrigin = DerivativeAddressRegistrationOrigin;
660
	type SovereignAccountDispatcherOrigin = EnsureRoot<AccountId>;
661
	type CurrencyId = CurrencyId;
662
	type AccountIdToLocation = AccountIdToLocation<AccountId>;
663
	type CurrencyIdToLocation = CurrencyIdToLocation<(
664
		EvmForeignAssets,
665
		AsAssetType<AssetId, AssetType, AssetManager>,
666
	)>;
667
	type XcmSender = XcmRouter;
668
	type SelfLocation = SelfLocation;
669
	type Weigher = XcmWeigher;
670
	type UniversalLocation = UniversalLocation;
671
	type BaseXcmWeight = BaseXcmWeight;
672
	type AssetTransactor = AssetTransactors;
673
	type ReserveProvider = AbsoluteAndRelativeReserve<SelfLocationAbsolute>;
674
	type WeightInfo = moonbeam_weights::pallet_xcm_transactor::WeightInfo<Runtime>;
675
	type HrmpManipulatorOrigin = GeneralAdminOrRoot;
676
	type HrmpOpenOrigin = FastGeneralAdminOrRoot;
677
	type MaxHrmpFee = xcm_builder::Case<MaxHrmpRelayFee>;
678
}
679

            
680
parameter_types! {
681
	// This is the relative view of erc20 assets.
682
	// Identified by this prefix + AccountKey20(contractAddress)
683
	// We use the RELATIVE multilocation
684
	pub Erc20XcmBridgePalletLocation: Location = Location {
685
		parents:0,
686
		interior: [
687
			PalletInstance(<Erc20XcmBridge as PalletInfoAccess>::index() as u8)
688
		].into()
689
	};
690

            
691
	// To be able to support almost all erc20 implementations,
692
	// we provide a sufficiently hight gas limit.
693
	pub Erc20XcmBridgeTransferGasLimit: u64 = 400_000;
694
}
695

            
696
impl pallet_erc20_xcm_bridge::Config for Runtime {
697
	type AccountIdConverter = LocationToH160;
698
	type Erc20MultilocationPrefix = Erc20XcmBridgePalletLocation;
699
	type Erc20TransferGasLimit = Erc20XcmBridgeTransferGasLimit;
700
	type EvmRunner = EvmRunnerPrecompileOrEthXcm<MoonbeamCall, Self>;
701
}
702

            
703
pub struct AccountIdToH160;
704
impl sp_runtime::traits::Convert<AccountId, H160> for AccountIdToH160 {
705
144
	fn convert(account_id: AccountId) -> H160 {
706
144
		account_id.into()
707
144
	}
708
}
709

            
710
pub struct EvmForeignAssetIdFilter;
711
impl frame_support::traits::Contains<AssetId> for EvmForeignAssetIdFilter {
712
18
	fn contains(asset_id: &AssetId) -> bool {
713
		use xcm_primitives::AssetTypeGetter as _;
714
		// We should return true only if the AssetId doesn't exist in AssetManager
715
18
		AssetManager::get_asset_type(*asset_id).is_none()
716
18
	}
717
}
718

            
719
pub type ForeignAssetManagerOrigin = EitherOf<
720
	MapSuccessToXcm<EnsureXcm<AllowSiblingParachains>>,
721
	MapSuccessToGovernance<
722
		EitherOf<
723
			EnsureRoot<AccountId>,
724
			EitherOf<
725
				pallet_collective::EnsureProportionMoreThan<
726
					AccountId,
727
					OpenTechCommitteeInstance,
728
					5,
729
					9,
730
				>,
731
				EitherOf<
732
					governance::custom_origins::FastGeneralAdmin,
733
					governance::custom_origins::GeneralAdmin,
734
				>,
735
			>,
736
		>,
737
	>,
738
>;
739

            
740
impl pallet_moonbeam_foreign_assets::Config for Runtime {
741
	type AccountIdToH160 = AccountIdToH160;
742
	type AssetIdFilter = EvmForeignAssetIdFilter;
743
	type EvmRunner = EvmRunnerPrecompileOrEthXcm<MoonbeamCall, Self>;
744
	type ConvertLocation =
745
		SiblingParachainConvertsVia<polkadot_parachain::primitives::Sibling, AccountId>;
746
	type ForeignAssetCreatorOrigin = ForeignAssetManagerOrigin;
747
	type ForeignAssetFreezerOrigin = ForeignAssetManagerOrigin;
748
	type ForeignAssetModifierOrigin = ForeignAssetManagerOrigin;
749
	type ForeignAssetUnfreezerOrigin = ForeignAssetManagerOrigin;
750
	type OnForeignAssetCreated = ();
751
	type MaxForeignAssets = ConstU32<256>;
752
	type RuntimeEvent = RuntimeEvent;
753
	type WeightInfo = moonbeam_weights::pallet_moonbeam_foreign_assets::WeightInfo<Runtime>;
754
	type XcmLocationToH160 = LocationToH160;
755
	type ForeignAssetCreationDeposit = dynamic_params::xcm_config::ForeignAssetCreationDeposit;
756
	type Balance = Balance;
757
	type Currency = Balances;
758
}
759

            
760
pub struct AssetFeesFilter;
761
impl frame_support::traits::Contains<Location> for AssetFeesFilter {
762
18
	fn contains(location: &Location) -> bool {
763
18
		location.parent_count() > 0
764
18
			&& location.first_interior() != Erc20XcmBridgePalletLocation::get().first_interior()
765
18
	}
766
}
767

            
768
pub type AddAndEditSupportedAssetOrigin = EitherOfDiverse<
769
	EnsureRoot<AccountId>,
770
	EitherOfDiverse<
771
		pallet_collective::EnsureProportionMoreThan<AccountId, OpenTechCommitteeInstance, 5, 9>,
772
		EitherOf<
773
			governance::custom_origins::GeneralAdmin,
774
			governance::custom_origins::FastGeneralAdmin,
775
		>,
776
	>,
777
>;
778

            
779
pub type RemoveSupportedAssetOrigin = EitherOfDiverse<
780
	EnsureRoot<AccountId>,
781
	pallet_collective::EnsureProportionMoreThan<AccountId, OpenTechCommitteeInstance, 5, 9>,
782
>;
783

            
784
impl pallet_xcm_weight_trader::Config for Runtime {
785
	type AccountIdToLocation = AccountIdToLocation<AccountId>;
786
	type AddSupportedAssetOrigin = AddAndEditSupportedAssetOrigin;
787
	type AssetLocationFilter = AssetFeesFilter;
788
	type AssetTransactor = AssetTransactors;
789
	type Balance = Balance;
790
	type EditSupportedAssetOrigin = AddAndEditSupportedAssetOrigin;
791
	type NativeLocation = SelfReserve;
792
	type PauseSupportedAssetOrigin = AddAndEditSupportedAssetOrigin;
793
	type ResumeSupportedAssetOrigin = AddAndEditSupportedAssetOrigin;
794
	type RemoveSupportedAssetOrigin = RemoveSupportedAssetOrigin;
795
	type RuntimeEvent = RuntimeEvent;
796
	type WeightInfo = moonbeam_weights::pallet_xcm_weight_trader::WeightInfo<Runtime>;
797
	type WeightToFee = <Runtime as pallet_transaction_payment::Config>::WeightToFee;
798
	type XcmFeesAccount = XcmFeesAccount;
799
	#[cfg(feature = "runtime-benchmarks")]
800
	type NotFilteredLocation = RelayLocation;
801
}
802

            
803
#[cfg(feature = "runtime-benchmarks")]
804
mod testing {
805
	use super::*;
806

            
807
	/// This From exists for benchmarking purposes. It has the potential side-effect of calling
808
	/// AssetManager::set_asset_type_asset_id() and should NOT be used in any production code.
809
	impl From<Location> for CurrencyId {
810
		fn from(location: Location) -> CurrencyId {
811
			use xcm_primitives::AssetTypeGetter;
812

            
813
			// If it does not exist, for benchmarking purposes, we create the association
814
			let asset_id = if let Some(asset_id) =
815
				AsAssetType::<AssetId, AssetType, AssetManager>::convert_location(&location)
816
			{
817
				asset_id
818
			} else {
819
				let asset_type: AssetType = location
820
					.try_into()
821
					.expect("Location convertion to AssetType should succeed");
822
				let asset_id: AssetId = asset_type.clone().into();
823
				AssetManager::set_asset_type_asset_id(asset_type, asset_id);
824
				asset_id
825
			};
826

            
827
			CurrencyId::ForeignAsset(asset_id)
828
		}
829
	}
830
}