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, 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, DecodeWithMemTracking, 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
parameter_types! {
85
	// The network Id of the relay
86
	pub RelayNetwork: NetworkId = NetworkId::ByGenesis(xcm::v5::WESTEND_GENESIS_HASH);
87
}
88

            
89
parameter_types! {
90
	// The relay chain Origin type
91
	pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into();
92
	// The universal location within the global consensus system
93
	pub UniversalLocation: InteriorLocation =
94
		[GlobalConsensus(RelayNetwork::get()), Parachain(ParachainInfo::parachain_id().into())].into();
95
	// Self Reserve location, defines the multilocation identifiying the self-reserve currency
96
	// This is used to match it also against our Balances pallet when we receive such
97
	// a Location: (Self Balances pallet index)
98
	// We use the RELATIVE multilocation
99
	pub SelfReserve: Location = Location {
100
		parents: 0,
101
		interior: [
102
			PalletInstance(<Balances as PalletInfoAccess>::index() as u8)
103
		].into()
104
	};
105
}
106

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

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

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

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

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

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

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

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

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

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

            
220
moonbeam_runtime_common::impl_evm_runner_precompile_or_eth_xcm!();
221

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

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

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

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

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

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

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

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

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

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

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

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

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

            
346
35
		current_relay_block_number >= ahm_relay_block
347
35
	}
348
}
349

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
613
}
614

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

            
620
// For now we only allow to transact in the relay, although this might change in the future
621
// Transactors just defines the chains in which we allow transactions to be issued through
622
// xcm
623
#[derive(
624
7
	Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo, DecodeWithMemTracking,
625
)]
626
pub enum Transactors {
627
	Relay,
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
			_ => Err(()),
644
		}
645
	}
646
}
647

            
648
impl UtilityEncodeCall for Transactors {
649
14
	fn encode_call(self, call: UtilityAvailableCalls) -> Vec<u8> {
650
14
		match self {
651
14
			Transactors::Relay => pallet_xcm_transactor::Pallet::<Runtime>::encode_call(
652
14
				pallet_xcm_transactor::Pallet(sp_std::marker::PhantomData::<Runtime>),
653
14
				call,
654
14
			),
655
14
		}
656
14
	}
657
}
658

            
659
impl XcmTransact for Transactors {
660
14
	fn destination(self) -> Location {
661
14
		match self {
662
14
			Transactors::Relay => Location::parent(),
663
14
		}
664
14
	}
665
}
666

            
667
pub type DerivativeAddressRegistrationOrigin =
668
	EitherOfDiverse<EnsureRoot<AccountId>, governance::custom_origins::GeneralAdmin>;
669

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

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

            
703
	// To be able to support almost all erc20 implementations,
704
	// we provide a sufficiently hight gas limit.
705
	pub Erc20XcmBridgeTransferGasLimit: u64 = 400_000;
706
}
707

            
708
impl pallet_erc20_xcm_bridge::Config for Runtime {
709
	type AccountIdConverter = LocationToH160;
710
	type Erc20MultilocationPrefix = Erc20XcmBridgePalletLocation;
711
	type Erc20TransferGasLimit = Erc20XcmBridgeTransferGasLimit;
712
	type EvmRunner = EvmRunnerPrecompileOrEthXcm<MoonbeamCall, Self>;
713
}
714

            
715
pub struct AccountIdToH160;
716
impl sp_runtime::traits::Convert<AccountId, H160> for AccountIdToH160 {
717
378
	fn convert(account_id: AccountId) -> H160 {
718
378
		account_id.into()
719
378
	}
720
}
721

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

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

            
763
pub struct AssetFeesFilter;
764
impl frame_support::traits::Contains<Location> for AssetFeesFilter {
765
42
	fn contains(location: &Location) -> bool {
766
42
		location.parent_count() > 0
767
42
			&& location.first_interior() != Erc20XcmBridgePalletLocation::get().first_interior()
768
42
	}
769
}
770

            
771
pub type AddAndEditSupportedAssetOrigin = EitherOfDiverse<
772
	EnsureRoot<AccountId>,
773
	EitherOfDiverse<
774
		pallet_collective::EnsureProportionMoreThan<AccountId, OpenTechCommitteeInstance, 5, 9>,
775
		EitherOf<
776
			governance::custom_origins::GeneralAdmin,
777
			governance::custom_origins::FastGeneralAdmin,
778
		>,
779
	>,
780
>;
781

            
782
pub type RemoveSupportedAssetOrigin = EitherOfDiverse<
783
	EnsureRoot<AccountId>,
784
	pallet_collective::EnsureProportionMoreThan<AccountId, OpenTechCommitteeInstance, 5, 9>,
785
>;
786

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

            
806
#[cfg(feature = "runtime-benchmarks")]
807
mod testing {
808
	use super::*;
809

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

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

            
827
				EvmForeignAssets::set_asset(location, asset_id);
828
				asset_id
829
			};
830

            
831
			CurrencyId::ForeignAsset(asset_id)
832
		}
833
	}
834
}