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::moonbase_weights;
21
use super::{
22
	governance, AccountId, AssetId, AssetManager, Balance, Balances, EmergencyParaXcm,
23
	Erc20XcmBridge, EvmForeignAssets, MaintenanceMode, MessageQueue, ParachainInfo,
24
	ParachainSystem, Perbill, PolkadotXcm, Runtime, RuntimeBlockWeights, RuntimeCall, RuntimeEvent,
25
	RuntimeOrigin, Treasury, XcmpQueue,
26
};
27
use crate::OpenTechCommitteeInstance;
28
use moonkit_xcm_primitives::AccountIdAssetIdConversion;
29
use sp_runtime::{
30
	traits::{Hash as THash, MaybeEquivalence, PostDispatchInfoOf},
31
	DispatchErrorWithPostInfo,
32
};
33

            
34
use frame_support::{
35
	parameter_types,
36
	traits::{EitherOf, EitherOfDiverse, Everything, Nothing, PalletInfoAccess, TransformOrigin},
37
};
38

            
39
use frame_system::{EnsureRoot, RawOrigin};
40
use sp_core::{ConstU32, H160, H256};
41
use sp_weights::Weight;
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

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

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

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

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

            
84
#[cfg(any(feature = "bridge-stagenet", feature = "bridge-betanet"))]
85
parameter_types! {
86
	// The network Id of the relay
87
	pub RelayNetwork: NetworkId = crate::bridge_config::SourceGlobalConsensusNetwork::get();
88
}
89

            
90
#[cfg(not(any(feature = "bridge-stagenet", feature = "bridge-betanet")))]
91
parameter_types! {
92
	// The network Id of the relay
93
	pub RelayNetwork: NetworkId = NetworkId::ByGenesis(xcm::v5::WESTEND_GENESIS_HASH);
94
}
95

            
96
parameter_types! {
97
	// The relay chain Origin type
98
	pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into();
99
	// The universal location within the global consensus system
100
	pub UniversalLocation: InteriorLocation =
101
		[GlobalConsensus(RelayNetwork::get()), Parachain(ParachainInfo::parachain_id().into())].into();
102

            
103

            
104
	// Self Reserve location, defines the multilocation identifiying the self-reserve currency
105
	// This is used to match it also against our Balances pallet when we receive such
106
	// a Location: (Self Balances pallet index)
107
	// We use the RELATIVE multilocation
108
	pub SelfReserve: Location = Location {
109
		parents: 0,
110
		interior: [
111
			PalletInstance(<Balances as PalletInfoAccess>::index() as u8)
112
		].into()
113
	};
114
}
115

            
116
/// Type for specifying how a `Location` can be converted into an `AccountId`. This is used
117
/// when determining ownership of accounts for asset transacting, when attempting to use XCM
118
/// `Transact` in order to determine the dispatch Origin, and when validating foreign assets
119
/// creation and ownership through the moonbeam_foreign_assets pallet.
120
pub type LocationToAccountId = (
121
	// The parent (Relay-chain) origin converts to the default `AccountId`.
122
	ParentIsPreset<AccountId>,
123
	// Sibling parachain origins convert to AccountId via the `ParaId::into`.
124
	SiblingParachainConvertsVia<polkadot_parachain::primitives::Sibling, AccountId>,
125
	// If we receive a Location of type AccountKey20, just generate a native account
126
	AccountKey20Aliases<RelayNetwork, AccountId>,
127
	// Foreign locations alias into accounts according to a hash of their standard description.
128
	HashedDescription<AccountId, DescribeFamily<DescribeAllTerminal>>,
129
	// Different global consensus parachain sovereign account.
130
	// (Used for over-bridge transfers and reserve processing)
131
	GlobalConsensusParachainConvertsFor<UniversalLocation, AccountId>,
132
);
133

            
134
/// Wrapper type around `LocationToAccountId` to convert an `AccountId` to type `H160`.
135
pub struct LocationToH160;
136
impl ConvertLocation<H160> for LocationToH160 {
137
28
	fn convert_location(location: &Location) -> Option<H160> {
138
28
		<LocationToAccountId as ConvertLocation<AccountId>>::convert_location(location)
139
28
			.map(Into::into)
140
28
	}
141
}
142

            
143
// The non-reserve fungible transactor type
144
// It will use pallet-assets, and the Id will be matched against AsAssetType
145
// This is intended to match FOREIGN ASSETS
146
pub type ForeignFungiblesTransactor = FungiblesAdapter<
147
	// Use this fungibles implementation:
148
	super::Assets,
149
	// Use this currency when it is a fungible asset matching the given location or name:
150
	(
151
		ConvertedConcreteId<
152
			AssetId,
153
			Balance,
154
			AsAssetType<AssetId, AssetType, AssetManager>,
155
			JustTry,
156
		>,
157
	),
158
	// Do a simple punn to convert an AccountId20 Location into a native chain account ID:
159
	LocationToAccountId,
160
	// Our chain's account ID type (we can't get away without mentioning it explicitly):
161
	AccountId,
162
	// We dont allow teleports.
163
	NoChecking,
164
	// We dont track any teleports
165
	(),
166
>;
167

            
168
/// The transactor for our own chain currency.
169
pub type LocalAssetTransactor = XcmCurrencyAdapter<
170
	// Use this currency:
171
	Balances,
172
	// Use this currency when it is a fungible asset matching any of the locations in
173
	// SelfReserveRepresentations
174
	xcm_builder::IsConcrete<SelfReserve>,
175
	// We can convert the MultiLocations with our converter above:
176
	LocationToAccountId,
177
	// Our chain's account ID type (we can't get away without mentioning it explicitly):
178
	AccountId,
179
	// We dont allow teleport
180
	(),
181
>;
182

            
183
// We use all transactors
184
// These correspond to
185
// SelfReserve asset, both pre and post 0.9.16
186
// Foreign assets
187
// We can remove the Old reanchor once
188
// we import https://github.com/open-web3-stack/open-runtime-module-library/pull/708
189
pub type AssetTransactors = (
190
	LocalAssetTransactor,
191
	EvmForeignAssets,
192
	ForeignFungiblesTransactor,
193
	Erc20XcmBridge,
194
);
195

            
196
/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance,
197
/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can
198
/// biases the kind of local `Origin` it will become.
199
pub type XcmOriginToTransactDispatchOrigin = (
200
	// Sovereign account converter; this attempts to derive an `AccountId` from the origin location
201
	// using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for
202
	// foreign chains who want to have a local sovereign account on this chain which they control.
203
	SovereignSignedViaLocation<LocationToAccountId, RuntimeOrigin>,
204
	// Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when
205
	// recognised.
206
	RelayChainAsNative<RelayChainOrigin, RuntimeOrigin>,
207
	// Native converter for sibling Parachains; will convert to a `SiblingPara` origin when
208
	// recognised.
209
	SiblingParachainAsNative<cumulus_pallet_xcm::Origin, RuntimeOrigin>,
210
	// Xcm origins can be represented natively under the Xcm pallet's Xcm origin.
211
	pallet_xcm::XcmPassthrough<RuntimeOrigin>,
212
	// Xcm Origins defined by a Multilocation of type AccountKey20 can be converted to a 20 byte-
213
	// account local origin
214
	SignedAccountKey20AsNative<RelayNetwork, RuntimeOrigin>,
215
);
216

            
217
parameter_types! {
218
	/// Maximum number of instructions in a single XCM fragment. A sanity check against
219
	/// weight caculations getting too crazy.
220
	pub MaxInstructions: u32 = 100;
221
}
222

            
223
/// Xcm Weigher shared between multiple Xcm-related configs.
224
pub type XcmWeigher = WeightInfoBounds<
225
	crate::weights::xcm::XcmWeight<Runtime, RuntimeCall>,
226
	RuntimeCall,
227
	MaxInstructions,
228
>;
229

            
230
pub type XcmBarrier = TrailingSetTopicAsId<(
231
	// Weight that is paid for may be consumed.
232
	TakeWeightCredit,
233
	// Expected responses are OK.
234
	AllowKnownQueryResponses<PolkadotXcm>,
235
	WithComputedOrigin<
236
		(
237
			// If the message is one that immediately attemps to pay for execution, then allow it.
238
			AllowTopLevelPaidExecutionFrom<Everything>,
239
			// Subscriptions for version tracking are OK.
240
			AllowSubscriptionsFrom<Everything>,
241
		),
242
		UniversalLocation,
243
		ConstU32<8>,
244
	>,
245
)>;
246

            
247
parameter_types! {
248
	/// Xcm fees will go to the treasury account
249
	pub XcmFeesAccount: AccountId = Treasury::account_id();
250
}
251

            
252
// Our implementation of the Moonbeam Call
253
// Attaches the right origin in case the call is made to pallet-ethereum-xcm
254
#[cfg(not(feature = "evm-tracing"))]
255
moonbeam_runtime_common::impl_moonbeam_xcm_call!();
256
#[cfg(feature = "evm-tracing")]
257
moonbeam_runtime_common::impl_moonbeam_xcm_call_tracing!();
258

            
259
moonbeam_runtime_common::impl_evm_runner_precompile_or_eth_xcm!();
260

            
261
pub struct SafeCallFilter;
262
impl frame_support::traits::Contains<RuntimeCall> for SafeCallFilter {
263
	fn contains(_call: &RuntimeCall) -> bool {
264
		// TODO review
265
		// This needs to be addressed at EVM level
266
		true
267
	}
268
}
269

            
270
parameter_types! {
271
	/// Location of Asset Hub
272
	pub AssetHubLocation: Location = Location::new(1, [Parachain(1001)]);
273
	pub const RelayLocation: Location = Location::parent();
274
	pub RelayLocationFilter: AssetFilter = Wild(AllOf {
275
		fun: WildFungible,
276
		id: xcm::prelude::AssetId(RelayLocation::get()),
277
	});
278
	pub RelayChainNativeAssetFromAssetHub: (AssetFilter, Location) = (
279
		RelayLocationFilter::get(),
280
		AssetHubLocation::get()
281
	);
282
	pub const MaxAssetsIntoHolding: u32 = xcm_primitives::MAX_ASSETS;
283
}
284

            
285
#[cfg(any(feature = "bridge-stagenet", feature = "bridge-betanet"))]
286
type BridgedReserves = (
287
	// Assets held in reserve on Asset Hub.
288
	IsBridgedConcreteAssetFrom<AssetHubLocation>,
289
	// Assets bridged from TargetBridgeLocation
290
	IsBridgedConcreteAssetFrom<crate::bridge_config::TargetBridgeLocation>,
291
);
292
#[cfg(not(any(feature = "bridge-stagenet", feature = "bridge-betanet")))]
293
type BridgedReserves = (
294
	// Assets held in reserve on Asset Hub.
295
	IsBridgedConcreteAssetFrom<AssetHubLocation>,
296
);
297

            
298
type Reserves = (
299
	// Assets bridged from different consensus systems
300
	BridgedReserves,
301
	// Relaychain (DOT) from Asset Hub
302
	Case<RelayChainNativeAssetFromAssetHub>,
303
	// Assets which the reserve is the same as the origin.
304
	MultiNativeAsset<AbsoluteAndRelativeReserve<SelfLocationAbsolute>>,
305
);
306

            
307
pub struct XcmExecutorConfig;
308
impl xcm_executor::Config for XcmExecutorConfig {
309
	type RuntimeCall = RuntimeCall;
310
	type XcmSender = XcmRouter;
311
	// How to withdraw and deposit an asset.
312
	type AssetTransactor = AssetTransactors;
313
	type OriginConverter = XcmOriginToTransactDispatchOrigin;
314
	// Filter to the reserve withdraw operations
315
	// Whenever the reserve matches the relative or absolute value
316
	// of our chain, we always return the relative reserve
317
	type IsReserve = Reserves;
318
	type IsTeleporter = (); // No teleport
319
	type UniversalLocation = UniversalLocation;
320
	type Barrier = XcmBarrier;
321
	type Weigher = XcmWeigher;
322
	// As trader we use the XcmWeightTrader pallet.
323
	// For each foreign asset, the fee is computed based on its relative price (also
324
	// stored in the XcmWeightTrader pallet) against the native asset.
325
	// For the native asset fee is computed using WeightToFee implementation.
326
	type Trader = pallet_xcm_weight_trader::Trader<Runtime>;
327
	type ResponseHandler = PolkadotXcm;
328
	type SubscriptionService = PolkadotXcm;
329
	type AssetTrap = pallet_erc20_xcm_bridge::AssetTrapWrapper<PolkadotXcm, Runtime>;
330
	type AssetClaims = PolkadotXcm;
331
	type CallDispatcher = MoonbeamCall;
332
	type PalletInstancesInfo = crate::AllPalletsWithSystem;
333
	type MaxAssetsIntoHolding = MaxAssetsIntoHolding;
334
	type AssetLocker = ();
335
	type AssetExchanger = ();
336
	type FeeManager = ();
337

            
338
	#[cfg(any(feature = "bridge-stagenet", feature = "bridge-betanet"))]
339
	type MessageExporter = super::BridgeXcmOver;
340
	#[cfg(not(any(feature = "bridge-stagenet", feature = "bridge-betanet")))]
341
	type MessageExporter = ();
342
	#[cfg(any(feature = "bridge-stagenet", feature = "bridge-betanet"))]
343
	type UniversalAliases = crate::bridge_config::UniversalAliases;
344
	#[cfg(not(any(feature = "bridge-stagenet", feature = "bridge-betanet")))]
345
	type UniversalAliases = Nothing;
346
	type SafeCallFilter = SafeCallFilter;
347
	type Aliasers = Nothing;
348
	type TransactionalProcessor = xcm_builder::FrameTransactionalProcessor;
349
	type HrmpNewChannelOpenRequestHandler = ();
350
	type HrmpChannelAcceptedHandler = ();
351
	type HrmpChannelClosingHandler = ();
352
	type XcmRecorder = PolkadotXcm;
353
}
354

            
355
// Converts a Signed Local Origin into a Location
356
pub type LocalOriginToLocation = SignedToAccountId20<RuntimeOrigin, AccountId, RelayNetwork>;
357

            
358
/// For routing XCM messages which do not cross local consensus boundary.
359
pub type LocalXcmRouter = (
360
	// Two routers - use UMP to communicate with the relay chain:
361
	cumulus_primitives_utility::ParentAsUmp<ParachainSystem, PolkadotXcm, ()>,
362
	// ..and XCMP to communicate with the sibling chains.
363
	XcmpQueue,
364
);
365

            
366
/// The means for routing XCM messages which are not for local execution into the right message
367
/// queues.
368
#[cfg(any(feature = "bridge-stagenet", feature = "bridge-betanet"))]
369
pub type XcmRouter = WithUniqueTopic<(
370
	// For routing XCM messages which do not cross local consensus boundary.
371
	LocalXcmRouter,
372
	// Router that exports messages to be delivered to the bridge destination
373
	moonbeam_runtime_common::bridge::BridgeXcmRouter<
374
		xcm_builder::LocalExporter<crate::BridgeXcmOver, UniversalLocation>,
375
	>,
376
)>;
377

            
378
/// The means for routing XCM messages which are not for local execution into the right message
379
/// queues.
380
#[cfg(not(any(feature = "bridge-stagenet", feature = "bridge-betanet")))]
381
pub type XcmRouter = WithUniqueTopic<LocalXcmRouter>;
382

            
383
pub type XcmExecutor = pallet_erc20_xcm_bridge::XcmExecutorWrapper<
384
	XcmExecutorConfig,
385
	xcm_executor::XcmExecutor<XcmExecutorConfig>,
386
>;
387

            
388
impl pallet_xcm::Config for Runtime {
389
	type RuntimeEvent = RuntimeEvent;
390
	type SendXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
391
	type XcmRouter = XcmRouter;
392
	type ExecuteXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
393
	type XcmExecuteFilter = Everything;
394
	type XcmExecutor = XcmExecutor;
395
	type XcmTeleportFilter = Nothing;
396
	type XcmReserveTransferFilter = Everything;
397
	type Weigher = XcmWeigher;
398
	type UniversalLocation = UniversalLocation;
399
	type RuntimeOrigin = RuntimeOrigin;
400
	type RuntimeCall = RuntimeCall;
401
	const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
402
	type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
403
	type Currency = Balances;
404
	type CurrencyMatcher = ();
405
	type TrustedLockers = ();
406
	type SovereignAccountOf = LocationToAccountId;
407
	type MaxLockers = ConstU32<8>;
408
	type MaxRemoteLockConsumers = ConstU32<0>;
409
	type RemoteLockConsumerIdentifier = ();
410
	type WeightInfo = moonbase_weights::pallet_xcm::WeightInfo<Runtime>;
411
	type AdminOrigin = EnsureRoot<AccountId>;
412
}
413

            
414
impl cumulus_pallet_xcm::Config for Runtime {
415
	type RuntimeEvent = RuntimeEvent;
416
	type XcmExecutor = XcmExecutor;
417
}
418

            
419
impl cumulus_pallet_xcmp_queue::Config for Runtime {
420
	type RuntimeEvent = RuntimeEvent;
421
	type ChannelInfo = ParachainSystem;
422
	type VersionWrapper = PolkadotXcm;
423
	type XcmpQueue = TransformOrigin<MessageQueue, AggregateMessageOrigin, ParaId, ParaIdToSibling>;
424
	type MaxInboundSuspended = sp_core::ConstU32<1_000>;
425
	type ControllerOrigin = EnsureRoot<AccountId>;
426
	type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin;
427
	type WeightInfo = moonbase_weights::cumulus_pallet_xcmp_queue::WeightInfo<Runtime>;
428
	type PriceForSiblingDelivery = polkadot_runtime_common::xcm_sender::NoPriceForMessageDelivery<
429
		cumulus_primitives_core::ParaId,
430
	>;
431
	type MaxActiveOutboundChannels = ConstU32<128>;
432
	// Most on-chain HRMP channels are configured to use 102400 bytes of max message size, so we
433
	// need to set the page size larger than that until we reduce the channel size on-chain.
434
	type MaxPageSize = MessageQueueHeapSize;
435
}
436

            
437
parameter_types! {
438
	pub const RelayOrigin: AggregateMessageOrigin = AggregateMessageOrigin::Parent;
439
}
440

            
441
parameter_types! {
442
	/// The amount of weight (if any) which should be provided to the message queue for
443
	/// servicing enqueued items.
444
	///
445
	/// This may be legitimately `None` in the case that you will call
446
	/// `ServiceQueues::service_queues` manually.
447
	pub MessageQueueServiceWeight: Weight =
448
		Perbill::from_percent(25) * RuntimeBlockWeights::get().max_block;
449
	/// The maximum number of stale pages (i.e. of overweight messages) allowed before culling
450
	/// can happen. Once there are more stale pages than this, then historical pages may be
451
	/// dropped, even if they contain unprocessed overweight messages.
452
	pub const MessageQueueMaxStale: u32 = 8;
453
	/// The size of the page; this implies the maximum message size which can be sent.
454
	///
455
	/// A good value depends on the expected message sizes, their weights, the weight that is
456
	/// available for processing them and the maximal needed message size. The maximal message
457
	/// size is slightly lower than this as defined by [`MaxMessageLenOf`].
458
	pub const MessageQueueHeapSize: u32 = 103 * 1024;
459
}
460

            
461
impl pallet_message_queue::Config for Runtime {
462
	type RuntimeEvent = RuntimeEvent;
463
	#[cfg(feature = "runtime-benchmarks")]
464
	type MessageProcessor = pallet_message_queue::mock_helpers::NoopMessageProcessor<
465
		cumulus_primitives_core::AggregateMessageOrigin,
466
	>;
467
	#[cfg(not(feature = "runtime-benchmarks"))]
468
	type MessageProcessor = pallet_ethereum_xcm::MessageProcessorWrapper<
469
		xcm_builder::ProcessXcmMessage<AggregateMessageOrigin, XcmExecutor, RuntimeCall>,
470
	>;
471
	type Size = u32;
472
	type HeapSize = MessageQueueHeapSize;
473
	type MaxStale = MessageQueueMaxStale;
474
	type ServiceWeight = MessageQueueServiceWeight;
475
	// The XCMP queue pallet is only ever able to handle the `Sibling(ParaId)` origin:
476
	type QueueChangeHandler = NarrowOriginToSibling<XcmpQueue>;
477
	// NarrowOriginToSibling calls XcmpQueue's is_paused if Origin is sibling. Allows all other origins
478
	type QueuePausedQuery = EmergencyParaXcm;
479
	type WeightInfo = moonbase_weights::pallet_message_queue::WeightInfo<Runtime>;
480
	type IdleMaxServiceWeight = MessageQueueServiceWeight;
481
}
482

            
483
pub type FastAuthorizeUpgradeOrigin = EitherOfDiverse<
484
	EnsureRoot<AccountId>,
485
	pallet_collective::EnsureProportionAtLeast<AccountId, OpenTechCommitteeInstance, 5, 9>,
486
>;
487

            
488
pub type ResumeXcmOrigin = EitherOfDiverse<
489
	EnsureRoot<AccountId>,
490
	pallet_collective::EnsureProportionAtLeast<AccountId, OpenTechCommitteeInstance, 5, 9>,
491
>;
492

            
493
impl pallet_emergency_para_xcm::Config for Runtime {
494
	type RuntimeEvent = RuntimeEvent;
495
	type CheckAssociatedRelayNumber =
496
		cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases;
497
	type QueuePausedQuery = (MaintenanceMode, NarrowOriginToSibling<XcmpQueue>);
498
	type PausedThreshold = ConstU32<300>;
499
	type FastAuthorizeUpgradeOrigin = FastAuthorizeUpgradeOrigin;
500
	type PausedToNormalOrigin = ResumeXcmOrigin;
501
}
502

            
503
// Our AssetType. For now we only handle Xcm Assets
504
14
#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
505
pub enum AssetType {
506
22
	Xcm(xcm::v3::Location),
507
}
508
impl Default for AssetType {
509
	fn default() -> Self {
510
		Self::Xcm(xcm::v3::Location::here())
511
	}
512
}
513

            
514
impl From<xcm::v3::Location> for AssetType {
515
469
	fn from(location: xcm::v3::Location) -> Self {
516
469
		Self::Xcm(location)
517
469
	}
518
}
519

            
520
// This can be removed once we fully adopt xcm::v5 everywhere
521
impl TryFrom<Location> for AssetType {
522
	type Error = ();
523

            
524
77
	fn try_from(location: Location) -> Result<Self, Self::Error> {
525
77
		// Convert the V5 location to a V3 location
526
77
		match xcm::VersionedLocation::V5(location).into_version(xcm::v3::VERSION) {
527
77
			Ok(xcm::VersionedLocation::V3(loc)) => Ok(AssetType::Xcm(loc.into())),
528
			// Any other version or conversion error returns an error
529
			_ => Err(()),
530
		}
531
77
	}
532
}
533

            
534
impl Into<Option<xcm::v3::Location>> for AssetType {
535
119
	fn into(self) -> Option<xcm::v3::Location> {
536
119
		match self {
537
119
			Self::Xcm(location) => Some(location),
538
119
		}
539
119
	}
540
}
541

            
542
impl Into<Option<Location>> for AssetType {
543
	fn into(self) -> Option<Location> {
544
		match self {
545
			Self::Xcm(location) => {
546
				let versioned = xcm::VersionedLocation::V3(location);
547
				match versioned.into_version(xcm::latest::VERSION) {
548
					Ok(xcm::VersionedLocation::V5(loc)) => Some(loc),
549
					_ => None,
550
				}
551
			}
552
		}
553
	}
554
}
555

            
556
// Implementation on how to retrieve the AssetId from an AssetType
557
// We take it
558
impl From<AssetType> for AssetId {
559
469
	fn from(asset: AssetType) -> AssetId {
560
469
		match asset {
561
469
			AssetType::Xcm(id) => {
562
469
				let mut result: [u8; 16] = [0u8; 16];
563
469
				let hash: H256 = id.using_encoded(<Runtime as frame_system::Config>::Hashing::hash);
564
469
				result.copy_from_slice(&hash.as_fixed_bytes()[0..16]);
565
469
				u128::from_le_bytes(result)
566
469
			}
567
469
		}
568
469
	}
569
}
570

            
571
// Our currencyId. We distinguish for now between SelfReserve, and Others, defined by their Id.
572
21
#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
573
pub enum CurrencyId {
574
	// Our native token
575
	SelfReserve,
576
	// Assets representing other chains native tokens
577
	ForeignAsset(AssetId),
578
	// Erc20 token
579
	Erc20 { contract_address: H160 },
580
}
581

            
582
impl AccountIdToCurrencyId<AccountId, CurrencyId> for Runtime {
583
14
	fn account_to_currency_id(account: AccountId) -> Option<CurrencyId> {
584
14
		Some(match account {
585
			// the self-reserve currency is identified by the pallet-balances address
586
14
			a if a == H160::from_low_u64_be(2050).into() => CurrencyId::SelfReserve,
587
			// the rest of the currencies, by their corresponding erc20 address
588
7
			_ => match Runtime::account_to_asset_id(account) {
589
				// A foreign asset
590
7
				Some((_prefix, asset_id)) => CurrencyId::ForeignAsset(asset_id),
591
				// If no known prefix is identified, we consider that it's a "real" erc20 token
592
				// (i.e. managed by a real smart contract)
593
				None => CurrencyId::Erc20 {
594
					contract_address: account.into(),
595
				},
596
			},
597
		})
598
14
	}
599
}
600

            
601
// How to convert from CurrencyId to Location
602
pub struct CurrencyIdToLocation<AssetXConverter>(sp_std::marker::PhantomData<AssetXConverter>);
603
impl<AssetXConverter> sp_runtime::traits::Convert<CurrencyId, Option<Location>>
604
	for CurrencyIdToLocation<AssetXConverter>
605
where
606
	AssetXConverter: MaybeEquivalence<Location, AssetId>,
607
{
608
4
	fn convert(currency: CurrencyId) -> Option<Location> {
609
4
		match currency {
610
			CurrencyId::SelfReserve => {
611
1
				let multi: Location = SelfReserve::get();
612
1
				Some(multi)
613
			}
614
3
			CurrencyId::ForeignAsset(asset) => AssetXConverter::convert_back(&asset),
615
			CurrencyId::Erc20 { contract_address } => {
616
				let mut location = Erc20XcmBridgePalletLocation::get();
617
				location
618
					.push_interior(Junction::AccountKey20 {
619
						key: contract_address.0,
620
						network: None,
621
					})
622
					.ok();
623
				Some(location)
624
			}
625
		}
626
4
	}
627
}
628

            
629
parameter_types! {
630
	pub const BaseXcmWeight: Weight
631
		= Weight::from_parts(200_000_000u64, 0);
632
	pub const MaxAssetsForTransfer: usize = 2;
633
	// This is how we are going to detect whether the asset is a Reserve asset
634
	// This however is the chain part only
635
	pub SelfLocation: Location = Location::here();
636
	// We need this to be able to catch when someone is trying to execute a non-
637
	// cross-chain transfer in xtokens through the absolute path way
638
	pub SelfLocationAbsolute: Location = Location {
639
		parents:1,
640
		interior: [
641
			Parachain(ParachainInfo::parachain_id().into())
642
		].into()
643
	};
644

            
645
}
646

            
647
// 1 WND/ROC should be enough
648
parameter_types! {
649
	pub MaxHrmpRelayFee: Asset = (Location::parent(), 1_000_000_000_000u128).into();
650
}
651

            
652
// For now we only allow to transact in the relay, although this might change in the future
653
// Transactors just defines the chains in which we allow transactions to be issued through
654
// xcm
655
7
#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
656
pub enum Transactors {
657
	Relay,
658
}
659

            
660
// Default for benchmarking
661
#[cfg(feature = "runtime-benchmarks")]
662
impl Default for Transactors {
663
	fn default() -> Self {
664
		Transactors::Relay
665
	}
666
}
667

            
668
impl TryFrom<u8> for Transactors {
669
	type Error = ();
670
	fn try_from(value: u8) -> Result<Self, Self::Error> {
671
		match value {
672
			0u8 => Ok(Transactors::Relay),
673
			_ => Err(()),
674
		}
675
	}
676
}
677

            
678
impl UtilityEncodeCall for Transactors {
679
14
	fn encode_call(self, call: UtilityAvailableCalls) -> Vec<u8> {
680
14
		match self {
681
14
			Transactors::Relay => pallet_xcm_transactor::Pallet::<Runtime>::encode_call(
682
14
				pallet_xcm_transactor::Pallet(sp_std::marker::PhantomData::<Runtime>),
683
14
				call,
684
14
			),
685
14
		}
686
14
	}
687
}
688

            
689
impl XcmTransact for Transactors {
690
14
	fn destination(self) -> Location {
691
14
		match self {
692
14
			Transactors::Relay => Location::parent(),
693
14
		}
694
14
	}
695
}
696

            
697
pub type DerivativeAddressRegistrationOrigin =
698
	EitherOfDiverse<EnsureRoot<AccountId>, governance::custom_origins::GeneralAdmin>;
699

            
700
impl pallet_xcm_transactor::Config for Runtime {
701
	type RuntimeEvent = RuntimeEvent;
702
	type Balance = Balance;
703
	type Transactor = Transactors;
704
	type DerivativeAddressRegistrationOrigin = DerivativeAddressRegistrationOrigin;
705
	type SovereignAccountDispatcherOrigin = EnsureRoot<AccountId>;
706
	type CurrencyId = CurrencyId;
707
	type AccountIdToLocation = AccountIdToLocation<AccountId>;
708
	type CurrencyIdToLocation = CurrencyIdToLocation<(
709
		EvmForeignAssets,
710
		AsAssetType<AssetId, AssetType, AssetManager>,
711
	)>;
712
	type XcmSender = XcmRouter;
713
	type SelfLocation = SelfLocation;
714
	type Weigher = XcmWeigher;
715
	type UniversalLocation = UniversalLocation;
716
	type BaseXcmWeight = BaseXcmWeight;
717
	type AssetTransactor = AssetTransactors;
718
	type ReserveProvider = AbsoluteAndRelativeReserve<SelfLocationAbsolute>;
719
	type WeightInfo = moonbase_weights::pallet_xcm_transactor::WeightInfo<Runtime>;
720
	type HrmpManipulatorOrigin = GeneralAdminOrRoot;
721
	type HrmpOpenOrigin = FastGeneralAdminOrRoot;
722
	type MaxHrmpFee = xcm_builder::Case<MaxHrmpRelayFee>;
723
}
724

            
725
parameter_types! {
726
	// This is the relative view of erc20 assets.
727
	// Identified by this prefix + AccountKey20(contractAddress)
728
	// We use the RELATIVE multilocation
729
	pub Erc20XcmBridgePalletLocation: Location = Location {
730
		parents:0,
731
		interior: [
732
			PalletInstance(<Erc20XcmBridge as PalletInfoAccess>::index() as u8)
733
		].into()
734
	};
735

            
736
	// To be able to support almost all erc20 implementations,
737
	// we provide a sufficiently hight gas limit.
738
	pub Erc20XcmBridgeTransferGasLimit: u64 = 400_000;
739
}
740

            
741
impl pallet_erc20_xcm_bridge::Config for Runtime {
742
	type AccountIdConverter = LocationToH160;
743
	type Erc20MultilocationPrefix = Erc20XcmBridgePalletLocation;
744
	type Erc20TransferGasLimit = Erc20XcmBridgeTransferGasLimit;
745
	type EvmRunner = EvmRunnerPrecompileOrEthXcm<MoonbeamCall, Self>;
746
}
747

            
748
pub struct AccountIdToH160;
749
impl sp_runtime::traits::Convert<AccountId, H160> for AccountIdToH160 {
750
378
	fn convert(account_id: AccountId) -> H160 {
751
378
		account_id.into()
752
378
	}
753
}
754

            
755
pub struct EvmForeignAssetIdFilter;
756
impl frame_support::traits::Contains<AssetId> for EvmForeignAssetIdFilter {
757
56
	fn contains(asset_id: &AssetId) -> bool {
758
		use xcm_primitives::AssetTypeGetter as _;
759
		// We should return true only if the AssetId doesn't exist in AssetManager
760
56
		AssetManager::get_asset_type(*asset_id).is_none()
761
56
	}
762
}
763

            
764
pub type ForeignAssetManagerOrigin = EitherOf<
765
	MapSuccessToXcm<EnsureXcm<AllowSiblingParachains>>,
766
	MapSuccessToGovernance<
767
		EitherOf<
768
			EnsureRoot<AccountId>,
769
			EitherOf<
770
				pallet_collective::EnsureProportionMoreThan<
771
					AccountId,
772
					OpenTechCommitteeInstance,
773
					5,
774
					9,
775
				>,
776
				EitherOf<
777
					governance::custom_origins::FastGeneralAdmin,
778
					governance::custom_origins::GeneralAdmin,
779
				>,
780
			>,
781
		>,
782
	>,
783
>;
784

            
785
impl pallet_moonbeam_foreign_assets::Config for Runtime {
786
	type AccountIdToH160 = AccountIdToH160;
787
	type AssetIdFilter = EvmForeignAssetIdFilter;
788
	type EvmRunner = EvmRunnerPrecompileOrEthXcm<MoonbeamCall, Self>;
789
	type ConvertLocation =
790
		SiblingParachainConvertsVia<polkadot_parachain::primitives::Sibling, AccountId>;
791
	type ForeignAssetCreatorOrigin = ForeignAssetManagerOrigin;
792
	type ForeignAssetFreezerOrigin = ForeignAssetManagerOrigin;
793
	type ForeignAssetModifierOrigin = ForeignAssetManagerOrigin;
794
	type ForeignAssetUnfreezerOrigin = ForeignAssetManagerOrigin;
795
	type OnForeignAssetCreated = ();
796
	type MaxForeignAssets = ConstU32<256>;
797
	type RuntimeEvent = RuntimeEvent;
798
	type WeightInfo = moonbase_weights::pallet_moonbeam_foreign_assets::WeightInfo<Runtime>;
799
	type XcmLocationToH160 = LocationToH160;
800
	type ForeignAssetCreationDeposit = dynamic_params::xcm_config::ForeignAssetCreationDeposit;
801
	type Balance = Balance;
802
	type Currency = Balances;
803
}
804

            
805
pub struct AssetFeesFilter;
806
impl frame_support::traits::Contains<Location> for AssetFeesFilter {
807
42
	fn contains(location: &Location) -> bool {
808
42
		location.parent_count() > 0
809
42
			&& location.first_interior() != Erc20XcmBridgePalletLocation::get().first_interior()
810
42
	}
811
}
812

            
813
pub type AddAndEditSupportedAssetOrigin = EitherOfDiverse<
814
	EnsureRoot<AccountId>,
815
	EitherOfDiverse<
816
		pallet_collective::EnsureProportionMoreThan<AccountId, OpenTechCommitteeInstance, 5, 9>,
817
		EitherOf<
818
			governance::custom_origins::GeneralAdmin,
819
			governance::custom_origins::FastGeneralAdmin,
820
		>,
821
	>,
822
>;
823

            
824
pub type RemoveSupportedAssetOrigin = EitherOfDiverse<
825
	EnsureRoot<AccountId>,
826
	pallet_collective::EnsureProportionMoreThan<AccountId, OpenTechCommitteeInstance, 5, 9>,
827
>;
828

            
829
impl pallet_xcm_weight_trader::Config for Runtime {
830
	type AccountIdToLocation = AccountIdToLocation<AccountId>;
831
	type AddSupportedAssetOrigin = AddAndEditSupportedAssetOrigin;
832
	type AssetLocationFilter = AssetFeesFilter;
833
	type AssetTransactor = AssetTransactors;
834
	type Balance = Balance;
835
	type EditSupportedAssetOrigin = AddAndEditSupportedAssetOrigin;
836
	type NativeLocation = SelfReserve;
837
	type PauseSupportedAssetOrigin = AddAndEditSupportedAssetOrigin;
838
	type ResumeSupportedAssetOrigin = AddAndEditSupportedAssetOrigin;
839
	type RemoveSupportedAssetOrigin = RemoveSupportedAssetOrigin;
840
	type RuntimeEvent = RuntimeEvent;
841
	type WeightInfo = moonbase_weights::pallet_xcm_weight_trader::WeightInfo<Runtime>;
842
	type WeightToFee = <Runtime as pallet_transaction_payment::Config>::WeightToFee;
843
	type XcmFeesAccount = XcmFeesAccount;
844
	#[cfg(feature = "runtime-benchmarks")]
845
	type NotFilteredLocation = RelayLocation;
846
}
847

            
848
#[cfg(feature = "runtime-benchmarks")]
849
mod testing {
850
	use super::*;
851

            
852
	/// This From exists for benchmarking purposes. It has the potential side-effect of calling
853
	/// AssetManager::set_asset_type_asset_id() and should NOT be used in any production code.
854
	impl From<Location> for CurrencyId {
855
		fn from(location: Location) -> CurrencyId {
856
			use xcm_primitives::AssetTypeGetter;
857

            
858
			// If it does not exist, for benchmarking purposes, we create the association
859
			let asset_id = if let Some(asset_id) =
860
				AsAssetType::<AssetId, AssetType, AssetManager>::convert_location(&location)
861
			{
862
				asset_id
863
			} else {
864
				let asset_type: AssetType = location
865
					.try_into()
866
					.expect("Location convertion to AssetType should succeed");
867
				let asset_id: AssetId = asset_type.clone().into();
868
				AssetManager::set_asset_type_asset_id(asset_type, asset_id);
869
				asset_id
870
			};
871

            
872
			CurrencyId::ForeignAsset(asset_id)
873
		}
874
	}
875
}