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
	governance, AccountId, AssetId, AssetManager, Balance, Balances, EmergencyParaXcm,
22
	Erc20XcmBridge, EvmForeignAssets, MaintenanceMode, MessageQueue, OpenTechCommitteeInstance,
23
	ParachainInfo, ParachainSystem, Perbill, PolkadotXcm, Runtime, RuntimeBlockWeights,
24
	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, HashedDescription,
46
	NoChecking, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative,
47
	SiblingParachainConvertsVia, SignedAccountKey20AsNative, SovereignSignedViaLocation,
48
	TakeWeightCredit, TrailingSetTopicAsId, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic,
49
};
50

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

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

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

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

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

            
101
/// Type for specifying how a `Location` can be converted into an `AccountId`. This is used
102
/// when determining ownership of accounts for asset transacting and when attempting to use XCM
103
/// `Transact` in order to determine the dispatch Origin.
104
pub type LocationToAccountId = (
105
	// The parent (Relay-chain) origin converts to the default `AccountId`.
106
	ParentIsPreset<AccountId>,
107
	// Sibling parachain origins convert to AccountId via the `ParaId::into`.
108
	SiblingParachainConvertsVia<polkadot_parachain::primitives::Sibling, AccountId>,
109
	// If we receive a Location of type AccountKey20, just generate a native account
110
	AccountKey20Aliases<RelayNetwork, AccountId>,
111
	// Generate remote accounts according to polkadot standards
112
	HashedDescription<AccountId, DescribeFamily<DescribeAllTerminal>>,
113
);
114

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

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

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

            
163
// We use all transactors
164
pub type AssetTransactors = (
165
	LocalAssetTransactor,
166
	EvmForeignAssets,
167
	ForeignFungiblesTransactor,
168
	Erc20XcmBridge,
169
);
170

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

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

            
198
/// Xcm Weigher shared between multiple Xcm-related configs.
199
pub type XcmWeigher = WeightInfoBounds<
200
	moonbeam_xcm_benchmarks::weights::XcmWeight<Runtime, RuntimeCall>,
201
	RuntimeCall,
202
	MaxInstructions,
203
>;
204

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

            
222
parameter_types! {
223
	/// Xcm fees will go to the treasury account
224
	pub XcmFeesAccount: AccountId = Treasury::account_id();
225
}
226

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

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

            
251
type Reserves = (
252
	// Assets bridged from different consensus systems held in reserve on Asset Hub.
253
	IsBridgedConcreteAssetFrom<AssetHubLocation>,
254
	// Relaychain (DOT) from Asset Hub
255
	Case<RelayChainNativeAssetFromAssetHub>,
256
	// Assets which the reserve is the same as the origin.
257
	MultiNativeAsset<AbsoluteAndRelativeReserve<SelfLocationAbsolute>>,
258
);
259

            
260
// Our implementation of the Moonbeam Call
261
// Attachs the right origin in case the call is made to pallet-ethereum-xcm
262
#[cfg(not(feature = "evm-tracing"))]
263
moonbeam_runtime_common::impl_moonbeam_xcm_call!();
264
#[cfg(feature = "evm-tracing")]
265
moonbeam_runtime_common::impl_moonbeam_xcm_call_tracing!();
266

            
267
moonbeam_runtime_common::impl_evm_runner_precompile_or_eth_xcm!();
268

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

            
310
type XcmExecutor = pallet_erc20_xcm_bridge::XcmExecutorWrapper<
311
	XcmExecutorConfig,
312
	xcm_executor::XcmExecutor<XcmExecutorConfig>,
313
>;
314

            
315
// Converts a Signed Local Origin into a Location
316
pub type LocalOriginToLocation = SignedToAccountId20<RuntimeOrigin, AccountId, RelayNetwork>;
317

            
318
/// The means for routing XCM messages which are not for local execution into the right message
319
/// queues.
320
pub type XcmRouter = WithUniqueTopic<(
321
	// Two routers - use UMP to communicate with the relay chain:
322
	cumulus_primitives_utility::ParentAsUmp<ParachainSystem, PolkadotXcm, ()>,
323
	// ..and XCMP to communicate with the sibling chains.
324
	XcmpQueue,
325
)>;
326

            
327
impl pallet_xcm::Config for Runtime {
328
	type RuntimeEvent = RuntimeEvent;
329
	type SendXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
330
	type XcmRouter = XcmRouter;
331
	type ExecuteXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
332
	type XcmExecuteFilter = Nothing;
333
	type XcmExecutor = XcmExecutor;
334
	type XcmTeleportFilter = Nothing;
335
	type XcmReserveTransferFilter = Everything;
336
	type Weigher = XcmWeigher;
337
	type UniversalLocation = UniversalLocation;
338
	type RuntimeOrigin = RuntimeOrigin;
339
	type RuntimeCall = RuntimeCall;
340
	const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
341
	type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
342
	type Currency = Balances;
343
	type CurrencyMatcher = ();
344
	type TrustedLockers = ();
345
	type SovereignAccountOf = LocationToAccountId;
346
	type MaxLockers = ConstU32<8>;
347
	type MaxRemoteLockConsumers = ConstU32<0>;
348
	type RemoteLockConsumerIdentifier = ();
349
	type WeightInfo = moonbeam_weights::pallet_xcm::WeightInfo<Runtime>;
350
	type AdminOrigin = EnsureRoot<AccountId>;
351
}
352

            
353
impl cumulus_pallet_xcm::Config for Runtime {
354
	type RuntimeEvent = RuntimeEvent;
355
	type XcmExecutor = XcmExecutor;
356
}
357

            
358
impl cumulus_pallet_xcmp_queue::Config for Runtime {
359
	type RuntimeEvent = RuntimeEvent;
360
	type ChannelInfo = ParachainSystem;
361
	type VersionWrapper = PolkadotXcm;
362
	type XcmpQueue = TransformOrigin<MessageQueue, AggregateMessageOrigin, ParaId, ParaIdToSibling>;
363
	type MaxInboundSuspended = sp_core::ConstU32<1_000>;
364
	type ControllerOrigin = EnsureRoot<AccountId>;
365
	type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin;
366
	type WeightInfo = moonbeam_weights::cumulus_pallet_xcmp_queue::WeightInfo<Runtime>;
367
	type PriceForSiblingDelivery = polkadot_runtime_common::xcm_sender::NoPriceForMessageDelivery<
368
		cumulus_primitives_core::ParaId,
369
	>;
370
	type MaxActiveOutboundChannels = ConstU32<128>;
371
	// Most on-chain HRMP channels are configured to use 102400 bytes of max message size, so we
372
	// need to set the page size larger than that until we reduce the channel size on-chain.
373
	type MaxPageSize = MessageQueueHeapSize;
374
}
375

            
376
parameter_types! {
377
	pub const RelayOrigin: AggregateMessageOrigin = AggregateMessageOrigin::Parent;
378
}
379

            
380
parameter_types! {
381
	/// The amount of weight (if any) which should be provided to the message queue for
382
	/// servicing enqueued items.
383
	///
384
	/// This may be legitimately `None` in the case that you will call
385
	/// `ServiceQueues::service_queues` manually.
386
	pub MessageQueueServiceWeight: Weight =
387
		Perbill::from_percent(25) * RuntimeBlockWeights::get().max_block;
388
	/// The maximum number of stale pages (i.e. of overweight messages) allowed before culling
389
	/// can happen. Once there are more stale pages than this, then historical pages may be
390
	/// dropped, even if they contain unprocessed overweight messages.
391
	pub const MessageQueueMaxStale: u32 = 8;
392
	/// The size of the page; this implies the maximum message size which can be sent.
393
	///
394
	/// A good value depends on the expected message sizes, their weights, the weight that is
395
	/// available for processing them and the maximal needed message size. The maximal message
396
	/// size is slightly lower than this as defined by [`MaxMessageLenOf`].
397
	pub const MessageQueueHeapSize: u32 = 103 * 1024;
398
}
399

            
400
impl pallet_message_queue::Config for Runtime {
401
	type RuntimeEvent = RuntimeEvent;
402
	#[cfg(feature = "runtime-benchmarks")]
403
	type MessageProcessor = pallet_message_queue::mock_helpers::NoopMessageProcessor<
404
		cumulus_primitives_core::AggregateMessageOrigin,
405
	>;
406
	#[cfg(not(feature = "runtime-benchmarks"))]
407
	type MessageProcessor = pallet_ethereum_xcm::MessageProcessorWrapper<
408
		xcm_builder::ProcessXcmMessage<AggregateMessageOrigin, XcmExecutor, RuntimeCall>,
409
	>;
410
	type Size = u32;
411
	type HeapSize = MessageQueueHeapSize;
412
	type MaxStale = MessageQueueMaxStale;
413
	type ServiceWeight = MessageQueueServiceWeight;
414
	// The XCMP queue pallet is only ever able to handle the `Sibling(ParaId)` origin:
415
	type QueueChangeHandler = NarrowOriginToSibling<XcmpQueue>;
416
	// NarrowOriginToSibling calls XcmpQueue's is_paused if Origin is sibling. Allows all other origins
417
	type QueuePausedQuery = EmergencyParaXcm;
418
	type WeightInfo = moonbeam_weights::pallet_message_queue::WeightInfo<Runtime>;
419
	type IdleMaxServiceWeight = MessageQueueServiceWeight;
420
}
421

            
422
pub type FastAuthorizeUpgradeOrigin = EitherOfDiverse<
423
	EnsureRoot<AccountId>,
424
	pallet_collective::EnsureProportionAtLeast<AccountId, OpenTechCommitteeInstance, 5, 9>,
425
>;
426

            
427
pub type ResumeXcmOrigin = EitherOfDiverse<
428
	EnsureRoot<AccountId>,
429
	pallet_collective::EnsureProportionAtLeast<AccountId, OpenTechCommitteeInstance, 5, 9>,
430
>;
431

            
432
impl pallet_emergency_para_xcm::Config for Runtime {
433
	type RuntimeEvent = RuntimeEvent;
434
	type CheckAssociatedRelayNumber =
435
		cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases;
436
	type QueuePausedQuery = (MaintenanceMode, NarrowOriginToSibling<XcmpQueue>);
437
	type PausedThreshold = ConstU32<300>;
438
	type FastAuthorizeUpgradeOrigin = FastAuthorizeUpgradeOrigin;
439
	type PausedToNormalOrigin = ResumeXcmOrigin;
440
}
441

            
442
// Our AssetType. For now we only handle Xcm Assets
443
12
#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
444
pub enum AssetType {
445
26
	Xcm(xcm::v3::Location),
446
}
447
impl Default for AssetType {
448
	fn default() -> Self {
449
		Self::Xcm(xcm::v3::Location::here())
450
	}
451
}
452

            
453
impl From<xcm::v3::Location> for AssetType {
454
420
	fn from(location: xcm::v3::Location) -> Self {
455
420
		Self::Xcm(location)
456
420
	}
457
}
458

            
459
// This can be removed once we fully adopt xcm::v5 everywhere
460
impl TryFrom<Location> for AssetType {
461
	type Error = ();
462

            
463
54
	fn try_from(location: Location) -> Result<Self, Self::Error> {
464
54
		// Convert the V5 location to a V3 location
465
54
		match xcm::VersionedLocation::V5(location).into_version(xcm::v3::VERSION) {
466
54
			Ok(xcm::VersionedLocation::V3(loc)) => Ok(AssetType::Xcm(loc.into())),
467
			// Any other version or conversion error returns an error
468
			_ => Err(()),
469
		}
470
54
	}
471
}
472

            
473
impl Into<Option<xcm::v3::Location>> for AssetType {
474
132
	fn into(self) -> Option<xcm::v3::Location> {
475
132
		match self {
476
132
			Self::Xcm(location) => Some(location),
477
132
		}
478
132
	}
479
}
480

            
481
impl Into<Option<Location>> for AssetType {
482
	fn into(self) -> Option<Location> {
483
		match self {
484
			Self::Xcm(location) => {
485
				let versioned = xcm::VersionedLocation::V3(location);
486
				match versioned.into_version(xcm::latest::VERSION) {
487
					Ok(xcm::VersionedLocation::V5(loc)) => Some(loc),
488
					_ => None,
489
				}
490
			}
491
		}
492
	}
493
}
494

            
495
// Implementation on how to retrieve the AssetId from an AssetType
496
// We simply hash the AssetType and take the lowest 128 bits
497
impl From<AssetType> for AssetId {
498
540
	fn from(asset: AssetType) -> AssetId {
499
540
		match asset {
500
540
			AssetType::Xcm(id) => {
501
540
				let mut result: [u8; 16] = [0u8; 16];
502
540
				let hash: H256 = id.using_encoded(<Runtime as frame_system::Config>::Hashing::hash);
503
540
				result.copy_from_slice(&hash.as_fixed_bytes()[0..16]);
504
540
				u128::from_le_bytes(result)
505
540
			}
506
540
		}
507
540
	}
508
}
509

            
510
// Our currencyId. We distinguish for now between SelfReserve, and Others, defined by their Id.
511
18
#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
512
pub 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

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

            
568
parameter_types! {
569
	pub const BaseXcmWeight: Weight = Weight::from_parts(200_000_000u64, 0);
570
	pub const MaxAssetsForTransfer: usize = 2;
571

            
572
	// This is how we are going to detect whether the asset is a Reserve asset
573
	// This however is the chain part only
574
	pub SelfLocation: Location = Location::here();
575
	// We need this to be able to catch when someone is trying to execute a non-
576
	// cross-chain transfer in xtokens through the absolute path way
577
	pub SelfLocationAbsolute: Location = Location {
578
		parents:1,
579
		interior: [
580
			Parachain(ParachainInfo::parachain_id().into())
581
		].into()
582
	};
583
}
584

            
585
// 1 DOT should be enough
586
parameter_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
6
#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
594
pub enum Transactors {
595
	Relay,
596
}
597

            
598
// Default for benchmarking
599
#[cfg(feature = "runtime-benchmarks")]
600
impl Default for Transactors {
601
	fn default() -> Self {
602
		Transactors::Relay
603
	}
604
}
605

            
606
impl TryFrom<u8> for Transactors {
607
	type Error = ();
608
	fn try_from(value: u8) -> Result<Self, Self::Error> {
609
		match value {
610
			0u8 => Ok(Transactors::Relay),
611
			_ => Err(()),
612
		}
613
	}
614
}
615

            
616
impl UtilityEncodeCall for Transactors {
617
12
	fn encode_call(self, call: UtilityAvailableCalls) -> Vec<u8> {
618
12
		match self {
619
12
			Transactors::Relay => pallet_xcm_transactor::Pallet::<Runtime>::encode_call(
620
12
				pallet_xcm_transactor::Pallet(sp_std::marker::PhantomData::<Runtime>),
621
12
				call,
622
12
			),
623
12
		}
624
12
	}
625
}
626

            
627
impl XcmTransact for Transactors {
628
12
	fn destination(self) -> Location {
629
12
		match self {
630
12
			Transactors::Relay => Location::parent(),
631
12
		}
632
12
	}
633
}
634

            
635
pub type DerivativeAddressRegistrationOrigin =
636
	EitherOfDiverse<EnsureRoot<AccountId>, governance::custom_origins::GeneralAdmin>;
637

            
638
impl pallet_xcm_transactor::Config for Runtime {
639
	type RuntimeEvent = RuntimeEvent;
640
	type Balance = Balance;
641
	type Transactor = Transactors;
642
	type DerivativeAddressRegistrationOrigin = DerivativeAddressRegistrationOrigin;
643
	type SovereignAccountDispatcherOrigin = EnsureRoot<AccountId>;
644
	type CurrencyId = CurrencyId;
645
	type AccountIdToLocation = AccountIdToLocation<AccountId>;
646
	type CurrencyIdToLocation = CurrencyIdToLocation<(
647
		EvmForeignAssets,
648
		AsAssetType<AssetId, AssetType, AssetManager>,
649
	)>;
650
	type XcmSender = XcmRouter;
651
	type SelfLocation = SelfLocation;
652
	type Weigher = XcmWeigher;
653
	type UniversalLocation = UniversalLocation;
654
	type BaseXcmWeight = BaseXcmWeight;
655
	type AssetTransactor = AssetTransactors;
656
	type ReserveProvider = AbsoluteAndRelativeReserve<SelfLocationAbsolute>;
657
	type WeightInfo = moonbeam_weights::pallet_xcm_transactor::WeightInfo<Runtime>;
658
	type HrmpManipulatorOrigin = GeneralAdminOrRoot;
659
	type HrmpOpenOrigin = FastGeneralAdminOrRoot;
660
	type MaxHrmpFee = xcm_builder::Case<MaxHrmpRelayFee>;
661
}
662

            
663
parameter_types! {
664
	// This is the relative view of erc20 assets.
665
	// Identified by this prefix + AccountKey20(contractAddress)
666
	// We use the RELATIVE multilocation
667
	pub Erc20XcmBridgePalletLocation: Location = Location {
668
		parents:0,
669
		interior: [
670
			PalletInstance(<Erc20XcmBridge as PalletInfoAccess>::index() as u8)
671
		].into()
672
	};
673

            
674
	// To be able to support almost all erc20 implementations,
675
	// we provide a sufficiently hight gas limit.
676
	pub Erc20XcmBridgeTransferGasLimit: u64 = 400_000;
677
}
678

            
679
impl pallet_erc20_xcm_bridge::Config for Runtime {
680
	type AccountIdConverter = LocationToH160;
681
	type Erc20MultilocationPrefix = Erc20XcmBridgePalletLocation;
682
	type Erc20TransferGasLimit = Erc20XcmBridgeTransferGasLimit;
683
	type EvmRunner = EvmRunnerPrecompileOrEthXcm<MoonbeamCall, Self>;
684
}
685

            
686
pub struct AccountIdToH160;
687
impl sp_runtime::traits::Convert<AccountId, H160> for AccountIdToH160 {
688
108
	fn convert(account_id: AccountId) -> H160 {
689
108
		account_id.into()
690
108
	}
691
}
692

            
693
pub struct EvmForeignAssetIdFilter;
694
impl frame_support::traits::Contains<AssetId> for EvmForeignAssetIdFilter {
695
12
	fn contains(asset_id: &AssetId) -> bool {
696
		use xcm_primitives::AssetTypeGetter as _;
697
		// We should return true only if the AssetId doesn't exist in AssetManager
698
12
		AssetManager::get_asset_type(*asset_id).is_none()
699
12
	}
700
}
701

            
702
pub 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
>;
722

            
723
impl pallet_moonbeam_foreign_assets::Config for Runtime {
724
	type AccountIdToH160 = AccountIdToH160;
725
	type AssetIdFilter = EvmForeignAssetIdFilter;
726
	type EvmRunner = EvmRunnerPrecompileOrEthXcm<MoonbeamCall, Self>;
727
	type ConvertLocation =
728
		SiblingParachainConvertsVia<polkadot_parachain::primitives::Sibling, AccountId>;
729
	type ForeignAssetCreatorOrigin = ForeignAssetManagerOrigin;
730
	type ForeignAssetFreezerOrigin = ForeignAssetManagerOrigin;
731
	type ForeignAssetModifierOrigin = ForeignAssetManagerOrigin;
732
	type ForeignAssetUnfreezerOrigin = ForeignAssetManagerOrigin;
733
	type OnForeignAssetCreated = ();
734
	type MaxForeignAssets = ConstU32<256>;
735
	type RuntimeEvent = RuntimeEvent;
736
	type WeightInfo = moonbeam_weights::pallet_moonbeam_foreign_assets::WeightInfo<Runtime>;
737
	type XcmLocationToH160 = LocationToH160;
738
	type ForeignAssetCreationDeposit = dynamic_params::xcm_config::ForeignAssetCreationDeposit;
739
	type Balance = Balance;
740
	type Currency = Balances;
741
}
742

            
743
pub struct AssetFeesFilter;
744
impl frame_support::traits::Contains<Location> for AssetFeesFilter {
745
6
	fn contains(location: &Location) -> bool {
746
6
		location.parent_count() > 0
747
6
			&& location.first_interior() != Erc20XcmBridgePalletLocation::get().first_interior()
748
6
	}
749
}
750

            
751
pub type AddAndEditSupportedAssetOrigin = EitherOfDiverse<
752
	EnsureRoot<AccountId>,
753
	EitherOfDiverse<
754
		pallet_collective::EnsureProportionMoreThan<AccountId, OpenTechCommitteeInstance, 5, 9>,
755
		EitherOf<
756
			governance::custom_origins::GeneralAdmin,
757
			governance::custom_origins::FastGeneralAdmin,
758
		>,
759
	>,
760
>;
761

            
762
pub type RemoveSupportedAssetOrigin = EitherOfDiverse<
763
	EnsureRoot<AccountId>,
764
	pallet_collective::EnsureProportionMoreThan<AccountId, OpenTechCommitteeInstance, 5, 9>,
765
>;
766

            
767
impl pallet_xcm_weight_trader::Config for Runtime {
768
	type AccountIdToLocation = AccountIdToLocation<AccountId>;
769
	type AddSupportedAssetOrigin = AddAndEditSupportedAssetOrigin;
770
	type AssetLocationFilter = AssetFeesFilter;
771
	type AssetTransactor = AssetTransactors;
772
	type Balance = Balance;
773
	type EditSupportedAssetOrigin = AddAndEditSupportedAssetOrigin;
774
	type NativeLocation = SelfReserve;
775
	type PauseSupportedAssetOrigin = AddAndEditSupportedAssetOrigin;
776
	type ResumeSupportedAssetOrigin = AddAndEditSupportedAssetOrigin;
777
	type RemoveSupportedAssetOrigin = RemoveSupportedAssetOrigin;
778
	type RuntimeEvent = RuntimeEvent;
779
	type WeightInfo = moonbeam_weights::pallet_xcm_weight_trader::WeightInfo<Runtime>;
780
	type WeightToFee = <Runtime as pallet_transaction_payment::Config>::WeightToFee;
781
	type XcmFeesAccount = XcmFeesAccount;
782
	#[cfg(feature = "runtime-benchmarks")]
783
	type NotFilteredLocation = RelayLocation;
784
}
785

            
786
#[cfg(feature = "runtime-benchmarks")]
787
mod testing {
788
	use super::*;
789

            
790
	/// This From exists for benchmarking purposes. It has the potential side-effect of calling
791
	/// AssetManager::set_asset_type_asset_id() and should NOT be used in any production code.
792
	impl From<Location> for CurrencyId {
793
		fn from(location: Location) -> CurrencyId {
794
			use xcm_primitives::AssetTypeGetter;
795

            
796
			// If it does not exist, for benchmarking purposes, we create the association
797
			let asset_id = if let Some(asset_id) =
798
				AsAssetType::<AssetId, AssetType, AssetManager>::convert_location(&location)
799
			{
800
				asset_id
801
			} else {
802
				let asset_type: AssetType = location
803
					.try_into()
804
					.expect("Location convertion to AssetType should succeed");
805
				let asset_id: AssetId = asset_type.clone().into();
806
				AssetManager::set_asset_type_asset_id(asset_type, asset_id);
807
				asset_id
808
			};
809

            
810
			CurrencyId::ForeignAsset(asset_id)
811
		}
812
	}
813
}