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::latest::prelude::{
53
	AllOf, Asset, AssetFilter, GlobalConsensus, InteriorLocation, Junction, Location, NetworkId,
54
	PalletInstance, Parachain, Wild, WildFungible,
55
};
56

            
57
use xcm_executor::traits::{CallDispatcher, ConvertLocation, JustTry};
58

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

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

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

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

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

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

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

            
160
// We use all transactors
161
pub type AssetTransactors = (
162
	LocalAssetTransactor,
163
	EvmForeignAssets,
164
	ForeignFungiblesTransactor,
165
	Erc20XcmBridge,
166
);
167

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

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

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

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

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

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

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

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

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

            
264
6
moonbeam_runtime_common::impl_evm_runner_precompile_or_eth_xcm!();
265

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

            
307
type XcmExecutor = pallet_erc20_xcm_bridge::XcmExecutorWrapper<
308
	XcmExecutorConfig,
309
	xcm_executor::XcmExecutor<XcmExecutorConfig>,
310
>;
311

            
312
// Converts a Signed Local Origin into a Location
313
pub type LocalOriginToLocation = SignedToAccountId20<RuntimeOrigin, AccountId, RelayNetwork>;
314

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

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

            
350
impl cumulus_pallet_xcm::Config for Runtime {
351
	type RuntimeEvent = RuntimeEvent;
352
	type XcmExecutor = XcmExecutor;
353
}
354

            
355
impl 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 = moonbeam_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

            
373
parameter_types! {
374
	pub const RelayOrigin: AggregateMessageOrigin = AggregateMessageOrigin::Parent;
375
}
376

            
377
parameter_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

            
397
impl 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 = moonbeam_weights::pallet_message_queue::WeightInfo<Runtime>;
416
	type IdleMaxServiceWeight = MessageQueueServiceWeight;
417
}
418

            
419
pub type FastAuthorizeUpgradeOrigin = EitherOfDiverse<
420
	EnsureRoot<AccountId>,
421
	pallet_collective::EnsureProportionAtLeast<AccountId, OpenTechCommitteeInstance, 5, 9>,
422
>;
423

            
424
pub type ResumeXcmOrigin = EitherOfDiverse<
425
	EnsureRoot<AccountId>,
426
	pallet_collective::EnsureProportionAtLeast<AccountId, OpenTechCommitteeInstance, 5, 9>,
427
>;
428

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

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

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

            
456
// This can be removed once we fully adopt xcm::v4 everywhere
457
impl TryFrom<Location> for AssetType {
458
	type Error = ();
459
	fn try_from(location: Location) -> Result<Self, Self::Error> {
460
		Ok(Self::Xcm(location.try_into()?))
461
	}
462
}
463

            
464
impl Into<Option<xcm::v3::Location>> for AssetType {
465
132
	fn into(self) -> Option<xcm::v3::Location> {
466
132
		match self {
467
132
			Self::Xcm(location) => Some(location),
468
132
		}
469
132
	}
470
}
471

            
472
impl Into<Option<Location>> for AssetType {
473
	fn into(self) -> Option<Location> {
474
		match self {
475
			Self::Xcm(location) => {
476
				xcm_builder::WithLatestLocationConverter::convert_back(&location)
477
			}
478
		}
479
	}
480
}
481

            
482
// Implementation on how to retrieve the AssetId from an AssetType
483
// We simply hash the AssetType and take the lowest 128 bits
484
impl From<AssetType> for AssetId {
485
540
	fn from(asset: AssetType) -> AssetId {
486
540
		match asset {
487
540
			AssetType::Xcm(id) => {
488
540
				let mut result: [u8; 16] = [0u8; 16];
489
540
				let hash: H256 = id.using_encoded(<Runtime as frame_system::Config>::Hashing::hash);
490
540
				result.copy_from_slice(&hash.as_fixed_bytes()[0..16]);
491
540
				u128::from_le_bytes(result)
492
540
			}
493
540
		}
494
540
	}
495
}
496

            
497
// Our currencyId. We distinguish for now between SelfReserve, and Others, defined by their Id.
498
18
#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
499
pub enum CurrencyId {
500
	// Our native token
501
	SelfReserve,
502
	// Assets representing other chains native tokens
503
	ForeignAsset(AssetId),
504
	// Erc20 token
505
	Erc20 { contract_address: H160 },
506
}
507

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

            
555
parameter_types! {
556
	pub const BaseXcmWeight: Weight = Weight::from_parts(200_000_000u64, 0);
557
	pub const MaxAssetsForTransfer: usize = 2;
558

            
559
	// This is how we are going to detect whether the asset is a Reserve asset
560
	// This however is the chain part only
561
	pub SelfLocation: Location = Location::here();
562
	// We need this to be able to catch when someone is trying to execute a non-
563
	// cross-chain transfer in xtokens through the absolute path way
564
	pub SelfLocationAbsolute: Location = Location {
565
		parents:1,
566
		interior: [
567
			Parachain(ParachainInfo::parachain_id().into())
568
		].into()
569
	};
570
}
571

            
572
// 1 DOT should be enough
573
parameter_types! {
574
	pub MaxHrmpRelayFee: Asset = (Location::parent(), 1_000_000_000_000u128).into();
575
}
576

            
577
// For now we only allow to transact in the relay, although this might change in the future
578
// Transactors just defines the chains in which we allow transactions to be issued through
579
// xcm
580
6
#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
581
pub enum Transactors {
582
	Relay,
583
}
584

            
585
// Default for benchmarking
586
#[cfg(feature = "runtime-benchmarks")]
587
impl Default for Transactors {
588
	fn default() -> Self {
589
		Transactors::Relay
590
	}
591
}
592

            
593
impl TryFrom<u8> for Transactors {
594
	type Error = ();
595
	fn try_from(value: u8) -> Result<Self, Self::Error> {
596
		match value {
597
			0u8 => Ok(Transactors::Relay),
598
			_ => Err(()),
599
		}
600
	}
601
}
602

            
603
impl UtilityEncodeCall for Transactors {
604
12
	fn encode_call(self, call: UtilityAvailableCalls) -> Vec<u8> {
605
12
		match self {
606
12
			Transactors::Relay => pallet_xcm_transactor::Pallet::<Runtime>::encode_call(
607
12
				pallet_xcm_transactor::Pallet(sp_std::marker::PhantomData::<Runtime>),
608
12
				call,
609
12
			),
610
12
		}
611
12
	}
612
}
613

            
614
impl XcmTransact for Transactors {
615
12
	fn destination(self) -> Location {
616
12
		match self {
617
12
			Transactors::Relay => Location::parent(),
618
12
		}
619
12
	}
620
}
621

            
622
pub type DerivativeAddressRegistrationOrigin =
623
	EitherOfDiverse<EnsureRoot<AccountId>, governance::custom_origins::GeneralAdmin>;
624

            
625
impl pallet_xcm_transactor::Config for Runtime {
626
	type RuntimeEvent = RuntimeEvent;
627
	type Balance = Balance;
628
	type Transactor = Transactors;
629
	type DerivativeAddressRegistrationOrigin = DerivativeAddressRegistrationOrigin;
630
	type SovereignAccountDispatcherOrigin = EnsureRoot<AccountId>;
631
	type CurrencyId = CurrencyId;
632
	type AccountIdToLocation = AccountIdToLocation<AccountId>;
633
	type CurrencyIdToLocation = CurrencyIdToLocation<(
634
		EvmForeignAssets,
635
		AsAssetType<AssetId, AssetType, AssetManager>,
636
	)>;
637
	type XcmSender = XcmRouter;
638
	type SelfLocation = SelfLocation;
639
	type Weigher = XcmWeigher;
640
	type UniversalLocation = UniversalLocation;
641
	type BaseXcmWeight = BaseXcmWeight;
642
	type AssetTransactor = AssetTransactors;
643
	type ReserveProvider = AbsoluteAndRelativeReserve<SelfLocationAbsolute>;
644
	type WeightInfo = moonbeam_weights::pallet_xcm_transactor::WeightInfo<Runtime>;
645
	type HrmpManipulatorOrigin = GeneralAdminOrRoot;
646
	type HrmpOpenOrigin = FastGeneralAdminOrRoot;
647
	type MaxHrmpFee = xcm_builder::Case<MaxHrmpRelayFee>;
648
}
649

            
650
parameter_types! {
651
	// This is the relative view of erc20 assets.
652
	// Identified by this prefix + AccountKey20(contractAddress)
653
	// We use the RELATIVE multilocation
654
	pub Erc20XcmBridgePalletLocation: Location = Location {
655
		parents:0,
656
		interior: [
657
			PalletInstance(<Erc20XcmBridge as PalletInfoAccess>::index() as u8)
658
		].into()
659
	};
660

            
661
	// To be able to support almost all erc20 implementations,
662
	// we provide a sufficiently hight gas limit.
663
	pub Erc20XcmBridgeTransferGasLimit: u64 = 400_000;
664
}
665

            
666
impl pallet_erc20_xcm_bridge::Config for Runtime {
667
	type AccountIdConverter = LocationToH160;
668
	type Erc20MultilocationPrefix = Erc20XcmBridgePalletLocation;
669
	type Erc20TransferGasLimit = Erc20XcmBridgeTransferGasLimit;
670
	type EvmRunner = EvmRunnerPrecompileOrEthXcm<MoonbeamCall, Self>;
671
}
672

            
673
pub struct AccountIdToH160;
674
impl sp_runtime::traits::Convert<AccountId, H160> for AccountIdToH160 {
675
36
	fn convert(account_id: AccountId) -> H160 {
676
36
		account_id.into()
677
36
	}
678
}
679

            
680
pub struct EvmForeignAssetIdFilter;
681
impl frame_support::traits::Contains<AssetId> for EvmForeignAssetIdFilter {
682
6
	fn contains(asset_id: &AssetId) -> bool {
683
6
		use xcm_primitives::AssetTypeGetter as _;
684
6
		// We should return true only if the AssetId doesn't exist in AssetManager
685
6
		AssetManager::get_asset_type(*asset_id).is_none()
686
6
	}
687
}
688

            
689
pub type ForeignAssetManagerOrigin = EitherOf<
690
	MapSuccessToXcm<EnsureXcm<AllowSiblingParachains>>,
691
	MapSuccessToGovernance<
692
		EitherOf<
693
			EnsureRoot<AccountId>,
694
			EitherOf<
695
				pallet_collective::EnsureProportionMoreThan<
696
					AccountId,
697
					OpenTechCommitteeInstance,
698
					5,
699
					9,
700
				>,
701
				EitherOf<
702
					governance::custom_origins::FastGeneralAdmin,
703
					governance::custom_origins::GeneralAdmin,
704
				>,
705
			>,
706
		>,
707
	>,
708
>;
709

            
710
impl pallet_moonbeam_foreign_assets::Config for Runtime {
711
	type AccountIdToH160 = AccountIdToH160;
712
	type AssetIdFilter = EvmForeignAssetIdFilter;
713
	type EvmRunner = EvmRunnerPrecompileOrEthXcm<MoonbeamCall, Self>;
714
	type ConvertLocation =
715
		SiblingParachainConvertsVia<polkadot_parachain::primitives::Sibling, AccountId>;
716
	type ForeignAssetCreatorOrigin = ForeignAssetManagerOrigin;
717
	type ForeignAssetFreezerOrigin = ForeignAssetManagerOrigin;
718
	type ForeignAssetModifierOrigin = ForeignAssetManagerOrigin;
719
	type ForeignAssetUnfreezerOrigin = ForeignAssetManagerOrigin;
720
	type OnForeignAssetCreated = ();
721
	type MaxForeignAssets = ConstU32<256>;
722
	type RuntimeEvent = RuntimeEvent;
723
	type WeightInfo = moonbeam_weights::pallet_moonbeam_foreign_assets::WeightInfo<Runtime>;
724
	type XcmLocationToH160 = LocationToH160;
725
	type ForeignAssetCreationDeposit = dynamic_params::xcm_config::ForeignAssetCreationDeposit;
726
	type Balance = Balance;
727
	type Currency = Balances;
728
}
729

            
730
pub struct AssetFeesFilter;
731
impl frame_support::traits::Contains<Location> for AssetFeesFilter {
732
	fn contains(location: &Location) -> bool {
733
		location.parent_count() > 0
734
			&& location.first_interior() != Erc20XcmBridgePalletLocation::get().first_interior()
735
	}
736
}
737

            
738
pub type AddAndEditSupportedAssetOrigin = EitherOfDiverse<
739
	EnsureRoot<AccountId>,
740
	EitherOfDiverse<
741
		pallet_collective::EnsureProportionMoreThan<AccountId, OpenTechCommitteeInstance, 5, 9>,
742
		EitherOf<
743
			governance::custom_origins::GeneralAdmin,
744
			governance::custom_origins::FastGeneralAdmin,
745
		>,
746
	>,
747
>;
748

            
749
pub type RemoveSupportedAssetOrigin = EitherOfDiverse<
750
	EnsureRoot<AccountId>,
751
	pallet_collective::EnsureProportionMoreThan<AccountId, OpenTechCommitteeInstance, 5, 9>,
752
>;
753

            
754
impl pallet_xcm_weight_trader::Config for Runtime {
755
	type AccountIdToLocation = AccountIdToLocation<AccountId>;
756
	type AddSupportedAssetOrigin = AddAndEditSupportedAssetOrigin;
757
	type AssetLocationFilter = AssetFeesFilter;
758
	type AssetTransactor = AssetTransactors;
759
	type Balance = Balance;
760
	type EditSupportedAssetOrigin = AddAndEditSupportedAssetOrigin;
761
	type NativeLocation = SelfReserve;
762
	type PauseSupportedAssetOrigin = AddAndEditSupportedAssetOrigin;
763
	type ResumeSupportedAssetOrigin = AddAndEditSupportedAssetOrigin;
764
	type RemoveSupportedAssetOrigin = RemoveSupportedAssetOrigin;
765
	type RuntimeEvent = RuntimeEvent;
766
	type WeightInfo = moonbeam_weights::pallet_xcm_weight_trader::WeightInfo<Runtime>;
767
	type WeightToFee = <Runtime as pallet_transaction_payment::Config>::WeightToFee;
768
	type XcmFeesAccount = XcmFeesAccount;
769
	#[cfg(feature = "runtime-benchmarks")]
770
	type NotFilteredLocation = RelayLocation;
771
}
772

            
773
#[cfg(feature = "runtime-benchmarks")]
774
mod testing {
775
	use super::*;
776
	use xcm_builder::WithLatestLocationConverter;
777

            
778
	/// This From exists for benchmarking purposes. It has the potential side-effect of calling
779
	/// AssetManager::set_asset_type_asset_id() and should NOT be used in any production code.
780
	impl From<Location> for CurrencyId {
781
		fn from(location: Location) -> CurrencyId {
782
			use xcm_primitives::AssetTypeGetter;
783

            
784
			// If it does not exist, for benchmarking purposes, we create the association
785
			let asset_id = if let Some(asset_id) =
786
				AsAssetType::<AssetId, AssetType, AssetManager>::convert_location(&location)
787
			{
788
				asset_id
789
			} else {
790
				let asset_type = AssetType::Xcm(
791
					WithLatestLocationConverter::convert(&location).expect("convert to v3"),
792
				);
793
				let asset_id: AssetId = asset_type.clone().into();
794
				AssetManager::set_asset_type_asset_id(asset_type, asset_id);
795
				asset_id
796
			};
797

            
798
			CurrencyId::ForeignAsset(asset_id)
799
		}
800
	}
801
}