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, 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

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

            
64
use xcm_executor::traits::{CallDispatcher, ConvertLocation, JustTry};
65

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

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

            
86
parameter_types! {
87
	// The network Id of the relay
88
	pub const RelayNetwork: NetworkId = NetworkId::ByGenesis(WESTEND_GENESIS_HASH);
89
	// The relay chain Origin type
90
	pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into();
91
	// The universal location within the global consensus system
92
	pub UniversalLocation: InteriorLocation =
93
		[GlobalConsensus(RelayNetwork::get()), Parachain(ParachainInfo::parachain_id().into())].into();
94

            
95

            
96
	// Self Reserve location, defines the multilocation identifiying the self-reserve currency
97
	// This is used to match it also against our Balances pallet when we receive such
98
	// a Location: (Self Balances pallet index)
99
	// We use the RELATIVE multilocation
100
	pub SelfReserve: Location = Location {
101
		parents: 0,
102
		interior: [
103
			PalletInstance(<Balances as PalletInfoAccess>::index() as u8)
104
		].into()
105
	};
106
}
107

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

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

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

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

            
172
// We use all transactors
173
// These correspond to
174
// SelfReserve asset, both pre and post 0.9.16
175
// Foreign assets
176
// We can remove the Old reanchor once
177
// we import https://github.com/open-web3-stack/open-runtime-module-library/pull/708
178
pub type AssetTransactors = (
179
	LocalAssetTransactor,
180
	EvmForeignAssets,
181
	ForeignFungiblesTransactor,
182
	Erc20XcmBridge,
183
);
184

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

            
206
parameter_types! {
207
	/// Maximum number of instructions in a single XCM fragment. A sanity check against
208
	/// weight caculations getting too crazy.
209
	pub MaxInstructions: u32 = 100;
210
}
211

            
212
/// Xcm Weigher shared between multiple Xcm-related configs.
213
pub type XcmWeigher = WeightInfoBounds<
214
	moonbeam_xcm_benchmarks::weights::XcmWeight<Runtime, RuntimeCall>,
215
	RuntimeCall,
216
	MaxInstructions,
217
>;
218

            
219
pub type XcmBarrier = TrailingSetTopicAsId<(
220
	// Weight that is paid for may be consumed.
221
	TakeWeightCredit,
222
	// Expected responses are OK.
223
	AllowKnownQueryResponses<PolkadotXcm>,
224
	WithComputedOrigin<
225
		(
226
			// If the message is one that immediately attemps to pay for execution, then allow it.
227
			AllowTopLevelPaidExecutionFrom<Everything>,
228
			// Subscriptions for version tracking are OK.
229
			AllowSubscriptionsFrom<Everything>,
230
		),
231
		UniversalLocation,
232
		ConstU32<8>,
233
	>,
234
)>;
235

            
236
parameter_types! {
237
	/// Xcm fees will go to the treasury account
238
	pub XcmFeesAccount: AccountId = Treasury::account_id();
239
}
240

            
241
// Our implementation of the Moonbeam Call
242
// Attachs the right origin in case the call is made to pallet-ethereum-xcm
243
#[cfg(not(feature = "evm-tracing"))]
244
moonbeam_runtime_common::impl_moonbeam_xcm_call!();
245
#[cfg(feature = "evm-tracing")]
246
moonbeam_runtime_common::impl_moonbeam_xcm_call_tracing!();
247

            
248
moonbeam_runtime_common::impl_evm_runner_precompile_or_eth_xcm!();
249

            
250
pub struct SafeCallFilter;
251
impl frame_support::traits::Contains<RuntimeCall> for SafeCallFilter {
252
	fn contains(_call: &RuntimeCall) -> bool {
253
		// TODO review
254
		// This needs to be addressed at EVM level
255
		true
256
	}
257
}
258

            
259
parameter_types! {
260
	/// Location of Asset Hub
261
	pub AssetHubLocation: Location = Location::new(1, [Parachain(1001)]);
262
	pub const RelayLocation: Location = Location::parent();
263
	pub RelayLocationFilter: AssetFilter = Wild(AllOf {
264
		fun: WildFungible,
265
		id: xcm::prelude::AssetId(RelayLocation::get()),
266
	});
267
	pub RelayChainNativeAssetFromAssetHub: (AssetFilter, Location) = (
268
		RelayLocationFilter::get(),
269
		AssetHubLocation::get()
270
	);
271
	pub const MaxAssetsIntoHolding: u32 = xcm_primitives::MAX_ASSETS;
272
}
273

            
274
type Reserves = (
275
	// Assets bridged from different consensus systems held in reserve on Asset Hub.
276
	IsBridgedConcreteAssetFrom<AssetHubLocation>,
277
	// Relaychain (DOT) from Asset Hub
278
	Case<RelayChainNativeAssetFromAssetHub>,
279
	// Assets which the reserve is the same as the origin.
280
	MultiNativeAsset<AbsoluteAndRelativeReserve<SelfLocationAbsolute>>,
281
);
282

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

            
324
// Converts a Signed Local Origin into a Location
325
pub type LocalOriginToLocation = SignedToAccountId20<RuntimeOrigin, AccountId, RelayNetwork>;
326

            
327
/// The means for routing XCM messages which are not for local execution into the right message
328
/// queues.
329
pub type XcmRouter = WithUniqueTopic<(
330
	// Two routers - use UMP to communicate with the relay chain:
331
	cumulus_primitives_utility::ParentAsUmp<ParachainSystem, PolkadotXcm, ()>,
332
	// ..and XCMP to communicate with the sibling chains.
333
	XcmpQueue,
334
)>;
335

            
336
type XcmExecutor = pallet_erc20_xcm_bridge::XcmExecutorWrapper<
337
	XcmExecutorConfig,
338
	xcm_executor::XcmExecutor<XcmExecutorConfig>,
339
>;
340

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

            
367
impl cumulus_pallet_xcm::Config for Runtime {
368
	type RuntimeEvent = RuntimeEvent;
369
	type XcmExecutor = XcmExecutor;
370
}
371

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

            
390
parameter_types! {
391
	pub const RelayOrigin: AggregateMessageOrigin = AggregateMessageOrigin::Parent;
392
}
393

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

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

            
436
pub type FastAuthorizeUpgradeOrigin = EitherOfDiverse<
437
	EnsureRoot<AccountId>,
438
	pallet_collective::EnsureProportionAtLeast<AccountId, OpenTechCommitteeInstance, 5, 9>,
439
>;
440

            
441
pub type ResumeXcmOrigin = EitherOfDiverse<
442
	EnsureRoot<AccountId>,
443
	pallet_collective::EnsureProportionAtLeast<AccountId, OpenTechCommitteeInstance, 5, 9>,
444
>;
445

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

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

            
467
impl From<xcm::v3::Location> for AssetType {
468
469
	fn from(location: xcm::v3::Location) -> Self {
469
469
		Self::Xcm(location)
470
469
	}
471
}
472

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

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

            
487
impl Into<Option<xcm::v3::Location>> for AssetType {
488
119
	fn into(self) -> Option<xcm::v3::Location> {
489
119
		match self {
490
119
			Self::Xcm(location) => Some(location),
491
119
		}
492
119
	}
493
}
494

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

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

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

            
535
impl AccountIdToCurrencyId<AccountId, CurrencyId> for Runtime {
536
14
	fn account_to_currency_id(account: AccountId) -> Option<CurrencyId> {
537
14
		Some(match account {
538
			// the self-reserve currency is identified by the pallet-balances address
539
14
			a if a == H160::from_low_u64_be(2050).into() => CurrencyId::SelfReserve,
540
			// the rest of the currencies, by their corresponding erc20 address
541
7
			_ => match Runtime::account_to_asset_id(account) {
542
				// A foreign asset
543
7
				Some((_prefix, asset_id)) => CurrencyId::ForeignAsset(asset_id),
544
				// If no known prefix is identified, we consider that it's a "real" erc20 token
545
				// (i.e. managed by a real smart contract)
546
				None => CurrencyId::Erc20 {
547
					contract_address: account.into(),
548
				},
549
			},
550
		})
551
14
	}
552
}
553

            
554
// How to convert from CurrencyId to Location
555
pub struct CurrencyIdToLocation<AssetXConverter>(sp_std::marker::PhantomData<AssetXConverter>);
556
impl<AssetXConverter> sp_runtime::traits::Convert<CurrencyId, Option<Location>>
557
	for CurrencyIdToLocation<AssetXConverter>
558
where
559
	AssetXConverter: MaybeEquivalence<Location, AssetId>,
560
{
561
4
	fn convert(currency: CurrencyId) -> Option<Location> {
562
4
		match currency {
563
			CurrencyId::SelfReserve => {
564
1
				let multi: Location = SelfReserve::get();
565
1
				Some(multi)
566
			}
567
3
			CurrencyId::ForeignAsset(asset) => AssetXConverter::convert_back(&asset),
568
			CurrencyId::Erc20 { contract_address } => {
569
				let mut location = Erc20XcmBridgePalletLocation::get();
570
				location
571
					.push_interior(Junction::AccountKey20 {
572
						key: contract_address.0,
573
						network: None,
574
					})
575
					.ok();
576
				Some(location)
577
			}
578
		}
579
4
	}
580
}
581

            
582
parameter_types! {
583
	pub const BaseXcmWeight: Weight
584
		= Weight::from_parts(200_000_000u64, 0);
585
	pub const MaxAssetsForTransfer: usize = 2;
586
	// This is how we are going to detect whether the asset is a Reserve asset
587
	// This however is the chain part only
588
	pub SelfLocation: Location = Location::here();
589
	// We need this to be able to catch when someone is trying to execute a non-
590
	// cross-chain transfer in xtokens through the absolute path way
591
	pub SelfLocationAbsolute: Location = Location {
592
		parents:1,
593
		interior: [
594
			Parachain(ParachainInfo::parachain_id().into())
595
		].into()
596
	};
597

            
598
}
599

            
600
// 1 WND/ROC should be enough
601
parameter_types! {
602
	pub MaxHrmpRelayFee: Asset = (Location::parent(), 1_000_000_000_000u128).into();
603
}
604

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
825
			CurrencyId::ForeignAsset(asset_id)
826
		}
827
	}
828
}