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, Balance, Balances, EmergencyParaXcm, Erc20XcmBridge,
23
	EvmForeignAssets, MaintenanceMode, MessageQueue, ParachainInfo, ParachainSystem, Perbill,
24
	PolkadotXcm, Runtime, RuntimeBlockWeights, RuntimeCall, RuntimeEvent, RuntimeOrigin, Treasury,
25
	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, DescribeAllTerminal, DescribeFamily, EnsureXcmOrigin,
45
	FungibleAdapter as XcmCurrencyAdapter, GlobalConsensusParachainConvertsFor, HashedDescription,
46
	ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia,
47
	SignedAccountKey20AsNative, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId,
48
	WeightInfoBounds, WithComputedOrigin, WithUniqueTopic,
49
};
50

            
51
use parachains_common::message_queue::{NarrowOriginToSibling, ParaIdToSibling};
52

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

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

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

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

            
83
parameter_types! {
84
	// The network Id of the relay
85
	pub RelayNetwork: NetworkId = NetworkId::ByGenesis(xcm::v5::WESTEND_GENESIS_HASH);
86
}
87

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

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

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

            
133
/// The transactor for our own chain currency.
134
pub type LocalAssetTransactor = XcmCurrencyAdapter<
135
	// Use this currency:
136
	Balances,
137
	// Use this currency when it is a fungible asset matching any of the locations in
138
	// SelfReserveRepresentations
139
	xcm_builder::IsConcrete<SelfReserve>,
140
	// We can convert the MultiLocations with our converter above:
141
	LocationToAccountId,
142
	// Our chain's account ID type (we can't get away without mentioning it explicitly):
143
	AccountId,
144
	// We dont allow teleport
145
	(),
146
>;
147

            
148
// We use all transactors
149
// These correspond to
150
// SelfReserve asset, both pre and post 0.9.16
151
// Foreign assets
152
// We can remove the Old reanchor once
153
// we import https://github.com/open-web3-stack/open-runtime-module-library/pull/708
154
pub type AssetTransactors = (LocalAssetTransactor, EvmForeignAssets, Erc20XcmBridge);
155

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

            
177
parameter_types! {
178
	/// Maximum number of instructions in a single XCM fragment. A sanity check against
179
	/// weight caculations getting too crazy.
180
	pub MaxInstructions: u32 = 100;
181
}
182

            
183
/// Xcm Weigher shared between multiple Xcm-related configs.
184
pub type XcmWeigher = WeightInfoBounds<
185
	crate::weights::xcm::XcmWeight<Runtime, RuntimeCall>,
186
	RuntimeCall,
187
	MaxInstructions,
188
>;
189

            
190
pub type XcmBarrier = TrailingSetTopicAsId<(
191
	// Weight that is paid for may be consumed.
192
	TakeWeightCredit,
193
	// Expected responses are OK.
194
	AllowKnownQueryResponses<PolkadotXcm>,
195
	WithComputedOrigin<
196
		(
197
			// If the message is one that immediately attemps to pay for execution, then allow it.
198
			AllowTopLevelPaidExecutionFrom<Everything>,
199
			// Subscriptions for version tracking are OK.
200
			AllowSubscriptionsFrom<Everything>,
201
		),
202
		UniversalLocation,
203
		ConstU32<8>,
204
	>,
205
)>;
206

            
207
parameter_types! {
208
	/// Xcm fees will go to the treasury account
209
	pub XcmFeesAccount: AccountId = Treasury::account_id();
210
}
211

            
212
// Our implementation of the Moonbeam Call
213
// Attaches the right origin in case the call is made to pallet-ethereum-xcm
214
#[cfg(not(feature = "evm-tracing"))]
215
moonbeam_runtime_common::impl_moonbeam_xcm_call!();
216
#[cfg(feature = "evm-tracing")]
217
moonbeam_runtime_common::impl_moonbeam_xcm_call_tracing!();
218

            
219
moonbeam_runtime_common::impl_evm_runner_precompile_or_eth_xcm!();
220

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

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

            
245
type BridgedReserves = (
246
	// Assets held in reserve on Asset Hub.
247
	IsBridgedConcreteAssetFrom<AssetHubLocation>,
248
);
249

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

            
259
pub struct XcmExecutorConfig;
260
impl xcm_executor::Config for XcmExecutorConfig {
261
	type RuntimeCall = RuntimeCall;
262
	type XcmSender = XcmRouter;
263
	// How to withdraw and deposit an asset.
264
	type AssetTransactor = AssetTransactors;
265
	type OriginConverter = XcmOriginToTransactDispatchOrigin;
266
	// Filter to the reserve withdraw operations
267
	// Whenever the reserve matches the relative or absolute value
268
	// of our chain, we always return the relative reserve
269
	type IsReserve = Reserves;
270
	type IsTeleporter = (); // No teleport
271
	type UniversalLocation = UniversalLocation;
272
	type Barrier = XcmBarrier;
273
	type Weigher = XcmWeigher;
274
	// As trader we use the XcmWeightTrader pallet.
275
	// For each foreign asset, the fee is computed based on its relative price (also
276
	// stored in the XcmWeightTrader pallet) against the native asset.
277
	// For the native asset fee is computed using WeightToFee implementation.
278
	type Trader = pallet_xcm_weight_trader::Trader<Runtime>;
279
	type ResponseHandler = PolkadotXcm;
280
	type SubscriptionService = PolkadotXcm;
281
	type AssetTrap = pallet_erc20_xcm_bridge::AssetTrapWrapper<PolkadotXcm, Runtime>;
282
	type AssetClaims = PolkadotXcm;
283
	type CallDispatcher = MoonbeamCall;
284
	type PalletInstancesInfo = crate::AllPalletsWithSystem;
285
	type MaxAssetsIntoHolding = MaxAssetsIntoHolding;
286
	type AssetLocker = ();
287
	type AssetExchanger = ();
288
	type FeeManager = ();
289

            
290
	type MessageExporter = ();
291
	type UniversalAliases = Nothing;
292
	type SafeCallFilter = SafeCallFilter;
293
	type Aliasers = Nothing;
294
	type TransactionalProcessor = xcm_builder::FrameTransactionalProcessor;
295
	type HrmpNewChannelOpenRequestHandler = ();
296
	type HrmpChannelAcceptedHandler = ();
297
	type HrmpChannelClosingHandler = ();
298
	type XcmRecorder = PolkadotXcm;
299
	type XcmEventEmitter = PolkadotXcm;
300
}
301

            
302
// Converts a Signed Local Origin into a Location
303
pub type LocalOriginToLocation = SignedToAccountId20<RuntimeOrigin, AccountId, RelayNetwork>;
304

            
305
/// For routing XCM messages which do not cross local consensus boundary.
306
pub type LocalXcmRouter = (
307
	// Two routers - use UMP to communicate with the relay chain:
308
	cumulus_primitives_utility::ParentAsUmp<ParachainSystem, PolkadotXcm, ()>,
309
	// ..and XCMP to communicate with the sibling chains.
310
	XcmpQueue,
311
);
312

            
313
/// The means for routing XCM messages which are not for local execution into the right message
314
/// queues.
315
pub type XcmRouter = WithUniqueTopic<LocalXcmRouter>;
316

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

            
322
parameter_types! {
323
	/// AssetHub migration start block for development/testing environments
324
	///
325
	/// Set to 0 by default since Moonbase is not affected by the actual AssetHub migration.
326
	/// This can be overridden for testing migration scenarios in development.
327
	pub storage AssetHubMigrationStartsAtRelayBlock: u32 = 0;
328
}
329

            
330
/// AssetHub migration status provider for Moonbase
331
///
332
/// # Purpose
333
/// While Moonbase will not be affected by the actual AssetHub migration on Polkadot,
334
/// this implementation allows simulation and testing of migration behavior in
335
/// development environments.
336
pub struct AssetHubMigrationStarted;
337
impl Get<bool> for AssetHubMigrationStarted {
338
30
	fn get() -> bool {
339
		use cumulus_pallet_parachain_system::RelaychainDataProvider;
340
		use sp_runtime::traits::BlockNumberProvider;
341

            
342
30
		let ahm_relay_block = AssetHubMigrationStartsAtRelayBlock::get();
343
30
		let current_relay_block_number = RelaychainDataProvider::<Runtime>::current_block_number();
344
30

            
345
30
		current_relay_block_number >= ahm_relay_block
346
30
	}
347
}
348

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

            
377
impl cumulus_pallet_xcm::Config for Runtime {
378
	type RuntimeEvent = RuntimeEvent;
379
	type XcmExecutor = XcmExecutor;
380
}
381

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

            
400
parameter_types! {
401
	pub const RelayOrigin: AggregateMessageOrigin = AggregateMessageOrigin::Parent;
402
}
403

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

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

            
446
pub type FastAuthorizeUpgradeOrigin = EitherOfDiverse<
447
	EnsureRoot<AccountId>,
448
	pallet_collective::EnsureProportionAtLeast<AccountId, OpenTechCommitteeInstance, 5, 9>,
449
>;
450

            
451
pub type ResumeXcmOrigin = EitherOfDiverse<
452
	EnsureRoot<AccountId>,
453
	pallet_collective::EnsureProportionAtLeast<AccountId, OpenTechCommitteeInstance, 5, 9>,
454
>;
455

            
456
impl pallet_emergency_para_xcm::Config for Runtime {
457
	type RuntimeEvent = RuntimeEvent;
458
	type CheckAssociatedRelayNumber =
459
		cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases;
460
	type QueuePausedQuery = (MaintenanceMode, NarrowOriginToSibling<XcmpQueue>);
461
	type PausedThreshold = ConstU32<300>;
462
	type FastAuthorizeUpgradeOrigin = FastAuthorizeUpgradeOrigin;
463
	type PausedToNormalOrigin = ResumeXcmOrigin;
464
}
465

            
466
// Our AssetType. For now we only handle Xcm Assets
467
#[derive(
468
	Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo, DecodeWithMemTracking,
469
)]
470
pub enum AssetType {
471
	Xcm(xcm::v3::Location),
472
}
473
impl Default for AssetType {
474
	fn default() -> Self {
475
		Self::Xcm(xcm::v3::Location::here())
476
	}
477
}
478

            
479
impl From<xcm::v3::Location> for AssetType {
480
	fn from(location: xcm::v3::Location) -> Self {
481
		Self::Xcm(location)
482
	}
483
}
484

            
485
// This can be removed once we fully adopt xcm::v5 everywhere
486
impl TryFrom<Location> for AssetType {
487
	type Error = ();
488

            
489
66
	fn try_from(location: Location) -> Result<Self, Self::Error> {
490
66
		// Convert the V5 location to a V3 location
491
66
		match xcm::VersionedLocation::V5(location).into_version(xcm::v3::VERSION) {
492
66
			Ok(xcm::VersionedLocation::V3(loc)) => Ok(AssetType::Xcm(loc.into())),
493
			// Any other version or conversion error returns an error
494
			_ => Err(()),
495
		}
496
66
	}
497
}
498

            
499
impl Into<Option<xcm::v3::Location>> for AssetType {
500
	fn into(self) -> Option<xcm::v3::Location> {
501
		match self {
502
			Self::Xcm(location) => Some(location),
503
		}
504
	}
505
}
506

            
507
impl Into<Option<Location>> for AssetType {
508
	fn into(self) -> Option<Location> {
509
		match self {
510
			Self::Xcm(location) => {
511
				let versioned = xcm::VersionedLocation::V3(location);
512
				match versioned.into_version(xcm::latest::VERSION) {
513
					Ok(xcm::VersionedLocation::V5(loc)) => Some(loc),
514
					_ => None,
515
				}
516
			}
517
		}
518
	}
519
}
520

            
521
// Implementation on how to retrieve the AssetId from an AssetType
522
// We take it
523
impl From<AssetType> for AssetId {
524
186
	fn from(asset: AssetType) -> AssetId {
525
186
		match asset {
526
186
			AssetType::Xcm(id) => {
527
186
				let mut result: [u8; 16] = [0u8; 16];
528
186
				let hash: H256 = id.using_encoded(<Runtime as frame_system::Config>::Hashing::hash);
529
186
				result.copy_from_slice(&hash.as_fixed_bytes()[0..16]);
530
186
				u128::from_le_bytes(result)
531
186
			}
532
186
		}
533
186
	}
534
}
535

            
536
// Our currencyId. We distinguish for now between SelfReserve, and Others, defined by their Id.
537
#[derive(
538
18
	Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo, DecodeWithMemTracking,
539
)]
540
pub enum CurrencyId {
541
	// Our native token
542
	SelfReserve,
543
	// Assets representing other chains native tokens
544
	ForeignAsset(AssetId),
545
	// Erc20 token
546
	Erc20 { contract_address: H160 },
547
}
548

            
549
impl AccountIdToCurrencyId<AccountId, CurrencyId> for Runtime {
550
12
	fn account_to_currency_id(account: AccountId) -> Option<CurrencyId> {
551
12
		Some(match account {
552
			// the self-reserve currency is identified by the pallet-balances address
553
12
			a if a == H160::from_low_u64_be(2050).into() => CurrencyId::SelfReserve,
554
			// the rest of the currencies, by their corresponding erc20 address
555
6
			_ => match Runtime::account_to_asset_id(account) {
556
				// A foreign asset
557
6
				Some((_prefix, asset_id)) => CurrencyId::ForeignAsset(asset_id),
558
				// If no known prefix is identified, we consider that it's a "real" erc20 token
559
				// (i.e. managed by a real smart contract)
560
				None => CurrencyId::Erc20 {
561
					contract_address: account.into(),
562
				},
563
			},
564
		})
565
12
	}
566
}
567

            
568
// How to convert from CurrencyId to Location
569
pub struct CurrencyIdToLocation<AssetXConverter>(sp_std::marker::PhantomData<AssetXConverter>);
570
impl<AssetXConverter> sp_runtime::traits::Convert<CurrencyId, Option<Location>>
571
	for CurrencyIdToLocation<AssetXConverter>
572
where
573
	AssetXConverter: MaybeEquivalence<Location, AssetId>,
574
{
575
4
	fn convert(currency: CurrencyId) -> Option<Location> {
576
4
		match currency {
577
			CurrencyId::SelfReserve => {
578
1
				let multi: Location = SelfReserve::get();
579
1
				Some(multi)
580
			}
581
3
			CurrencyId::ForeignAsset(asset) => AssetXConverter::convert_back(&asset),
582
			CurrencyId::Erc20 { contract_address } => {
583
				let mut location = Erc20XcmBridgePalletLocation::get();
584
				location
585
					.push_interior(Junction::AccountKey20 {
586
						key: contract_address.0,
587
						network: None,
588
					})
589
					.ok();
590
				Some(location)
591
			}
592
		}
593
4
	}
594
}
595

            
596
parameter_types! {
597
	pub const BaseXcmWeight: Weight
598
		= Weight::from_parts(200_000_000u64, 0);
599
	pub const MaxAssetsForTransfer: usize = 2;
600
	// This is how we are going to detect whether the asset is a Reserve asset
601
	// This however is the chain part only
602
	pub SelfLocation: Location = Location::here();
603
	// We need this to be able to catch when someone is trying to execute a non-
604
	// cross-chain transfer in xtokens through the absolute path way
605
	pub SelfLocationAbsolute: Location = Location {
606
		parents:1,
607
		interior: [
608
			Parachain(ParachainInfo::parachain_id().into())
609
		].into()
610
	};
611

            
612
}
613

            
614
// 1 WND/ROC should be enough
615
parameter_types! {
616
	pub MaxHrmpRelayFee: Asset = (Location::parent(), 1_000_000_000_000u128).into();
617
}
618

            
619
// For now we only allow to transact in the relay, although this might change in the future
620
// Transactors just defines the chains in which we allow transactions to be issued through
621
// xcm
622
#[derive(
623
12
	Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo, DecodeWithMemTracking,
624
)]
625
pub enum Transactors {
626
	Relay,
627
	AssetHub,
628
}
629

            
630
// Default for benchmarking
631
#[cfg(feature = "runtime-benchmarks")]
632
impl Default for Transactors {
633
	fn default() -> Self {
634
		Transactors::Relay
635
	}
636
}
637

            
638
impl TryFrom<u8> for Transactors {
639
	type Error = ();
640
	fn try_from(value: u8) -> Result<Self, Self::Error> {
641
		match value {
642
			0u8 => Ok(Transactors::Relay),
643
			1u8 => Ok(Transactors::AssetHub),
644
			_ => Err(()),
645
		}
646
	}
647
}
648

            
649
impl XcmTransact for Transactors {
650
12
	fn destination(self) -> Location {
651
12
		match self {
652
12
			Transactors::Relay => RelayLocation::get(),
653
			Transactors::AssetHub => AssetHubLocation::get(),
654
		}
655
12
	}
656

            
657
36
	fn utility_pallet_index(&self) -> u8 {
658
36
		match self {
659
36
			Transactors::Relay => pallet_xcm_transactor::RelayIndices::<Runtime>::get().utility,
660
			Transactors::AssetHub => pallet_xcm_transactor::ASSET_HUB_UTILITY_PALLET_INDEX,
661
		}
662
36
	}
663

            
664
	fn staking_pallet_index(&self) -> u8 {
665
		match self {
666
			Transactors::Relay => pallet_xcm_transactor::RelayIndices::<Runtime>::get().staking,
667
			Transactors::AssetHub => pallet_xcm_transactor::ASSET_HUB_STAKING_PALLET_INDEX,
668
		}
669
	}
670
}
671

            
672
pub type DerivativeAddressRegistrationOrigin =
673
	EitherOfDiverse<EnsureRoot<AccountId>, governance::custom_origins::GeneralAdmin>;
674

            
675
impl pallet_xcm_transactor::Config for Runtime {
676
	type RuntimeEvent = RuntimeEvent;
677
	type Balance = Balance;
678
	type Transactor = Transactors;
679
	type DerivativeAddressRegistrationOrigin = DerivativeAddressRegistrationOrigin;
680
	type SovereignAccountDispatcherOrigin = EnsureRoot<AccountId>;
681
	type CurrencyId = CurrencyId;
682
	type AccountIdToLocation = AccountIdToLocation<AccountId>;
683
	type CurrencyIdToLocation = CurrencyIdToLocation<(EvmForeignAssets,)>;
684
	type XcmSender = XcmRouter;
685
	type SelfLocation = SelfLocation;
686
	type Weigher = XcmWeigher;
687
	type UniversalLocation = UniversalLocation;
688
	type BaseXcmWeight = BaseXcmWeight;
689
	type AssetTransactor = AssetTransactors;
690
	type ReserveProvider = AbsoluteAndRelativeReserve<SelfLocationAbsolute>;
691
	type WeightInfo = moonbase_weights::pallet_xcm_transactor::WeightInfo<Runtime>;
692
	type HrmpManipulatorOrigin = GeneralAdminOrRoot;
693
	type HrmpOpenOrigin = FastGeneralAdminOrRoot;
694
	type MaxHrmpFee = xcm_builder::Case<MaxHrmpRelayFee>;
695
}
696

            
697
parameter_types! {
698
	// This is the relative view of erc20 assets.
699
	// Identified by this prefix + AccountKey20(contractAddress)
700
	// We use the RELATIVE multilocation
701
	pub Erc20XcmBridgePalletLocation: Location = Location {
702
		parents:0,
703
		interior: [
704
			PalletInstance(<Erc20XcmBridge as PalletInfoAccess>::index() as u8)
705
		].into()
706
	};
707

            
708
	// To be able to support almost all erc20 implementations,
709
	// we provide a sufficiently hight gas limit.
710
	pub Erc20XcmBridgeTransferGasLimit: u64 = 400_000;
711
}
712

            
713
impl pallet_erc20_xcm_bridge::Config for Runtime {
714
	type AccountIdConverter = LocationToH160;
715
	type Erc20MultilocationPrefix = Erc20XcmBridgePalletLocation;
716
	type Erc20TransferGasLimit = Erc20XcmBridgeTransferGasLimit;
717
	type EvmRunner = EvmRunnerPrecompileOrEthXcm<MoonbeamCall, Self>;
718
}
719

            
720
pub struct AccountIdToH160;
721
impl sp_runtime::traits::Convert<AccountId, H160> for AccountIdToH160 {
722
324
	fn convert(account_id: AccountId) -> H160 {
723
324
		account_id.into()
724
324
	}
725
}
726

            
727
pub type ForeignAssetManagerOrigin = EitherOf<
728
	MapSuccessToXcm<EnsureXcm<AllowSiblingParachains>>,
729
	MapSuccessToGovernance<
730
		EitherOf<
731
			EnsureRoot<AccountId>,
732
			EitherOf<
733
				pallet_collective::EnsureProportionMoreThan<
734
					AccountId,
735
					OpenTechCommitteeInstance,
736
					5,
737
					9,
738
				>,
739
				EitherOf<
740
					governance::custom_origins::FastGeneralAdmin,
741
					governance::custom_origins::GeneralAdmin,
742
				>,
743
			>,
744
		>,
745
	>,
746
>;
747

            
748
impl pallet_moonbeam_foreign_assets::Config for Runtime {
749
	type AccountIdToH160 = AccountIdToH160;
750
	type AssetIdFilter = Everything;
751
	type EvmRunner = EvmRunnerPrecompileOrEthXcm<MoonbeamCall, Self>;
752
	type ConvertLocation =
753
		SiblingParachainConvertsVia<polkadot_parachain::primitives::Sibling, AccountId>;
754
	type ForeignAssetCreatorOrigin = ForeignAssetManagerOrigin;
755
	type ForeignAssetFreezerOrigin = ForeignAssetManagerOrigin;
756
	type ForeignAssetModifierOrigin = ForeignAssetManagerOrigin;
757
	type ForeignAssetUnfreezerOrigin = ForeignAssetManagerOrigin;
758
	type OnForeignAssetCreated = ();
759
	type MaxForeignAssets = ConstU32<256>;
760
	type RuntimeEvent = RuntimeEvent;
761
	type WeightInfo = moonbase_weights::pallet_moonbeam_foreign_assets::WeightInfo<Runtime>;
762
	type XcmLocationToH160 = LocationToH160;
763
	type ForeignAssetCreationDeposit = dynamic_params::xcm_config::ForeignAssetCreationDeposit;
764
	type Balance = Balance;
765
	type Currency = Balances;
766
}
767

            
768
pub struct AssetFeesFilter;
769
impl frame_support::traits::Contains<Location> for AssetFeesFilter {
770
36
	fn contains(location: &Location) -> bool {
771
36
		location.parent_count() > 0
772
36
			&& location.first_interior() != Erc20XcmBridgePalletLocation::get().first_interior()
773
36
	}
774
}
775

            
776
pub type AddAndEditSupportedAssetOrigin = EitherOfDiverse<
777
	EnsureRoot<AccountId>,
778
	EitherOfDiverse<
779
		pallet_collective::EnsureProportionMoreThan<AccountId, OpenTechCommitteeInstance, 5, 9>,
780
		EitherOf<
781
			governance::custom_origins::GeneralAdmin,
782
			governance::custom_origins::FastGeneralAdmin,
783
		>,
784
	>,
785
>;
786

            
787
pub type RemoveSupportedAssetOrigin = EitherOfDiverse<
788
	EnsureRoot<AccountId>,
789
	pallet_collective::EnsureProportionMoreThan<AccountId, OpenTechCommitteeInstance, 5, 9>,
790
>;
791

            
792
impl pallet_xcm_weight_trader::Config for Runtime {
793
	type AccountIdToLocation = AccountIdToLocation<AccountId>;
794
	type AddSupportedAssetOrigin = AddAndEditSupportedAssetOrigin;
795
	type AssetLocationFilter = AssetFeesFilter;
796
	type AssetTransactor = AssetTransactors;
797
	type Balance = Balance;
798
	type EditSupportedAssetOrigin = AddAndEditSupportedAssetOrigin;
799
	type NativeLocation = SelfReserve;
800
	type PauseSupportedAssetOrigin = AddAndEditSupportedAssetOrigin;
801
	type ResumeSupportedAssetOrigin = AddAndEditSupportedAssetOrigin;
802
	type RemoveSupportedAssetOrigin = RemoveSupportedAssetOrigin;
803
	type RuntimeEvent = RuntimeEvent;
804
	type WeightInfo = moonbase_weights::pallet_xcm_weight_trader::WeightInfo<Runtime>;
805
	type WeightToFee = <Runtime as pallet_transaction_payment::Config>::WeightToFee;
806
	type XcmFeesAccount = XcmFeesAccount;
807
	#[cfg(feature = "runtime-benchmarks")]
808
	type NotFilteredLocation = RelayLocation;
809
}
810

            
811
#[cfg(feature = "runtime-benchmarks")]
812
mod testing {
813
	use super::*;
814

            
815
	/// This From exists for benchmarking purposes. It has the potential side-effect of calling
816
	/// EvmForeignAssets::set_asset() and should NOT be used in any production code.
817
	impl From<Location> for CurrencyId {
818
		fn from(location: Location) -> CurrencyId {
819
			use sp_runtime::traits::MaybeEquivalence;
820

            
821
			// If it does not exist, for benchmarking purposes, we create the association
822
			let asset_id = if let Some(asset_id) = EvmForeignAssets::convert(&location) {
823
				asset_id
824
			} else {
825
				// Generate asset ID from location hash (similar to old AssetManager approach)
826
				let hash: H256 =
827
					location.using_encoded(<Runtime as frame_system::Config>::Hashing::hash);
828
				let mut result: [u8; 16] = [0u8; 16];
829
				result.copy_from_slice(&hash.as_fixed_bytes()[0..16]);
830
				let asset_id = u128::from_le_bytes(result);
831

            
832
				EvmForeignAssets::set_asset(location, asset_id);
833
				asset_id
834
			};
835

            
836
			CurrencyId::ForeignAsset(asset_id)
837
		}
838
	}
839
}