1
// Copyright 2019-2025 PureStake Inc.
2
// This file is part of Moonbeam.
3

            
4
// Moonbeam is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Moonbeam is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Moonbeam.  If not, see <http://www.gnu.org/licenses/>.
16

            
17
//! XCM configuration for Moonbase.
18
//!
19

            
20
use super::{
21
	bridge_config, governance, AccountId, AssetId, Balance, Balances, BridgeXcmOverMoonriver,
22
	EmergencyParaXcm, Erc20XcmBridge, EvmForeignAssets, MaintenanceMode, MessageQueue,
23
	OpenTechCommitteeInstance, ParachainInfo, ParachainSystem, Perbill, PolkadotXcm, Runtime,
24
	RuntimeBlockWeights, RuntimeCall, RuntimeEvent, RuntimeOrigin, Treasury, XcmpQueue,
25
};
26

            
27
use super::moonbeam_weights;
28
use frame_support::{
29
	parameter_types,
30
	traits::{EitherOf, EitherOfDiverse, Everything, Nothing, PalletInfoAccess, TransformOrigin},
31
};
32
use moonkit_xcm_primitives::AccountIdAssetIdConversion;
33
use sp_runtime::{
34
	traits::{Hash as THash, MaybeEquivalence, PostDispatchInfoOf},
35
	DispatchErrorWithPostInfo,
36
};
37
use sp_weights::Weight;
38

            
39
use frame_system::{EnsureRoot, RawOrigin};
40
use sp_core::{ConstU32, H160, H256};
41

            
42
use xcm_builder::{
43
	AccountKey20Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom,
44
	AllowTopLevelPaidExecutionFrom, Case, 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
use xcm::{
53
	latest::prelude::{
54
		AllOf, Asset, AssetFilter, GlobalConsensus, InteriorLocation, Junction, Location,
55
		NetworkId, PalletInstance, Parachain, Wild, WildFungible,
56
	},
57
	IntoVersion,
58
};
59

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

            
62
use cumulus_primitives_core::{AggregateMessageOrigin, ParaId};
63
use frame_support::traits::Disabled;
64
use pallet_xcm::EnsureXcm;
65
use xcm_primitives::{
66
	AbsoluteAndRelativeReserve, AccountIdToCurrencyId, AccountIdToLocation,
67
	IsBridgedConcreteAssetFrom, MultiNativeAsset, SignedToAccountId20, UtilityAvailableCalls,
68
	UtilityEncodeCall, 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 const RelayNetwork: NetworkId = NetworkId::Polkadot;
86
	// The relay chain Origin type
87
	pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into();
88
	pub UniversalLocation: InteriorLocation =
89
		[GlobalConsensus(RelayNetwork::get()), Parachain(ParachainInfo::parachain_id().into())].into();
90
	// Self Reserve location, defines the multilocation identifiying the self-reserve currency
91
	// This is used to match it also against our Balances pallet when we receive such
92
	// a Location: (Self Balances pallet index)
93
	// We use the RELATIVE multilocation
94
	pub SelfReserve: Location = Location {
95
		parents:0,
96
		interior: [
97
			PalletInstance(<Balances as PalletInfoAccess>::index() as u8)
98
		].into()
99
	};
100
}
101

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

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

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

            
143
// We use all transactors
144
pub type AssetTransactors = (LocalAssetTransactor, EvmForeignAssets, Erc20XcmBridge);
145

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

            
167
parameter_types! {
168
	/// Maximum number of instructions in a single XCM fragment. A sanity check against
169
	/// weight caculations getting too crazy.
170
	pub MaxInstructions: u32 = 100;
171
}
172

            
173
/// Xcm Weigher shared between multiple Xcm-related configs.
174
pub type XcmWeigher = WeightInfoBounds<
175
	crate::weights::xcm::XcmWeight<Runtime, RuntimeCall>,
176
	RuntimeCall,
177
	MaxInstructions,
178
>;
179

            
180
pub type XcmBarrier = TrailingSetTopicAsId<(
181
	// Weight that is paid for may be consumed.
182
	TakeWeightCredit,
183
	// Expected responses are OK.
184
	AllowKnownQueryResponses<PolkadotXcm>,
185
	WithComputedOrigin<
186
		(
187
			// If the message is one that immediately attemps to pay for execution, then allow it.
188
			AllowTopLevelPaidExecutionFrom<Everything>,
189
			// Subscriptions for version tracking are OK.
190
			AllowSubscriptionsFrom<Everything>,
191
		),
192
		UniversalLocation,
193
		ConstU32<8>,
194
	>,
195
)>;
196

            
197
parameter_types! {
198
	/// Xcm fees will go to the treasury account
199
	pub XcmFeesAccount: AccountId = Treasury::account_id();
200
}
201

            
202
pub struct SafeCallFilter;
203
impl frame_support::traits::Contains<RuntimeCall> for SafeCallFilter {
204
	fn contains(_call: &RuntimeCall) -> bool {
205
		// TODO review
206
		// This needs to be addressed at EVM level
207
		true
208
	}
209
}
210

            
211
parameter_types! {
212
	 /// Location of Asset Hub
213
	pub AssetHubLocation: Location = Location::new(1, [Parachain(1000)]);
214
	pub const RelayLocation: Location = Location::parent();
215
	pub RelayLocationFilter: AssetFilter = Wild(AllOf {
216
		fun: WildFungible,
217
		id: xcm::prelude::AssetId(RelayLocation::get()),
218
	});
219
	pub RelayChainNativeAssetFromAssetHub: (AssetFilter, Location) = (
220
		RelayLocationFilter::get(),
221
		AssetHubLocation::get()
222
	);
223
	pub const MaxAssetsIntoHolding: u32 = xcm_primitives::MAX_ASSETS;
224
}
225

            
226
type Reserves = (
227
	// Assets bridged from different consensus systems held in reserve on Asset Hub.
228
	IsBridgedConcreteAssetFrom<AssetHubLocation>,
229
	// Assets bridged from Moonriver
230
	IsBridgedConcreteAssetFrom<bp_moonriver::GlobalConsensusLocation>,
231
	// Relaychain (DOT) from Asset Hub
232
	Case<RelayChainNativeAssetFromAssetHub>,
233
	// Assets which the reserve is the same as the origin.
234
	MultiNativeAsset<AbsoluteAndRelativeReserve<SelfLocationAbsolute>>,
235
);
236

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

            
244
moonbeam_runtime_common::impl_evm_runner_precompile_or_eth_xcm!();
245

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

            
288
pub type XcmExecutor = pallet_erc20_xcm_bridge::XcmExecutorWrapper<
289
	XcmExecutorConfig,
290
	xcm_executor::XcmExecutor<XcmExecutorConfig>,
291
>;
292

            
293
// Converts a Signed Local Origin into a Location
294
pub type LocalOriginToLocation = SignedToAccountId20<RuntimeOrigin, AccountId, RelayNetwork>;
295

            
296
/// For routing XCM messages which do not cross local consensus boundary.
297
pub type LocalXcmRouter = (
298
	// Two routers - use UMP to communicate with the relay chain:
299
	cumulus_primitives_utility::ParentAsUmp<ParachainSystem, PolkadotXcm, ()>,
300
	// ..and XCMP to communicate with the sibling chains.
301
	XcmpQueue,
302
);
303

            
304
/// The means for routing XCM messages which are not for local execution into the right message
305
/// queues.
306
pub type XcmRouter = WithUniqueTopic<(
307
	// The means for routing XCM messages which are not for local execution into the right message
308
	// queues.
309
	LocalXcmRouter,
310
	// Router that exports messages to be delivered to the Kusama GlobalConsensus
311
	moonbeam_runtime_common::bridge::BridgeXcmRouter<
312
		xcm_builder::LocalExporter<BridgeXcmOverMoonriver, UniversalLocation>,
313
	>,
314
)>;
315

            
316
parameter_types! {
317
	/// Conservative estimation for when AssetHub migration will start on Polkadot
318
	///
319
	/// # Calculation Details
320
	/// - **Computation date**: 2025-09-01 16:43:54 UTC
321
	/// - **Reference block**: 27_580_400
322
	/// - **Reference timestamp**: 1_756_741_434 (2025-09-01 16:43:54 UTC)
323
	/// - **Target date**: 2025-11-03 00:00:00 UTC (1 day before the migration)
324
	/// - **Target timestamp**: 1_762_128_000
325
	///
326
	/// # Block Estimation
327
	/// ```text
328
	/// Time difference: 1_762_128_000 - 1_756_741_434 = 5_386_566 seconds
329
	/// Estimated blocks: 5_386_566 ÷ 6 = 897_761 blocks (assuming 6s block time)
330
	/// Target block: 27_580_400 + 897_761 = 28_478_161
331
	/// ```
332
	///
333
	/// **Note**: This assumes consistent 6-second block times and no network delays.
334
	/// The actual migration is guaranteed to start no earlier than this block.
335
	///
336
	/// If the timeline changes, this value can be updated through a governance proposal.
337
	pub storage AssetHubMigrationStartsAtRelayBlock: u32 = 28_478_161;
338
}
339

            
340
pub struct AssetHubMigrationStarted;
341
impl Get<bool> for AssetHubMigrationStarted {
342
48
	fn get() -> bool {
343
		use cumulus_pallet_parachain_system::RelaychainDataProvider;
344
		use sp_runtime::traits::BlockNumberProvider;
345

            
346
48
		let ahm_relay_block = AssetHubMigrationStartsAtRelayBlock::get();
347
48
		let current_relay_block_number = RelaychainDataProvider::<Runtime>::current_block_number();
348
48

            
349
48
		current_relay_block_number >= ahm_relay_block
350
48
	}
351
}
352

            
353
impl pallet_xcm::Config for Runtime {
354
	type RuntimeEvent = RuntimeEvent;
355
	type SendXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
356
	type XcmRouter = XcmRouter;
357
	type ExecuteXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
358
	type XcmExecuteFilter = Nothing;
359
	type XcmExecutor = XcmExecutor;
360
	type XcmTeleportFilter = Nothing;
361
	type XcmReserveTransferFilter = Everything;
362
	type Weigher = XcmWeigher;
363
	type UniversalLocation = UniversalLocation;
364
	type RuntimeOrigin = RuntimeOrigin;
365
	type RuntimeCall = RuntimeCall;
366
	const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
367
	type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
368
	type Currency = Balances;
369
	type CurrencyMatcher = ();
370
	type TrustedLockers = ();
371
	type SovereignAccountOf = LocationToAccountId;
372
	type MaxLockers = ConstU32<8>;
373
	type MaxRemoteLockConsumers = ConstU32<0>;
374
	type RemoteLockConsumerIdentifier = ();
375
	type WeightInfo = moonbeam_weights::pallet_xcm::WeightInfo<Runtime>;
376
	type AdminOrigin = EnsureRoot<AccountId>;
377
	type AuthorizedAliasConsideration = Disabled;
378
	/// Configuration for pallet-xcm AssetHub migration timing
379
	///
380
	/// This type alias informs pallet-xcm when to enable DOT reserve checks
381
	/// introduced in [PR #9137](https://github.com/paritytech/polkadot-sdk/pull/9137).
382
	///
383
	/// # Migration Strategy
384
	/// Rather than immediately enforcing strict reserve checks (which would cause
385
	/// hard failures), this provides a grace period for dApps to update their
386
	/// implementations and adapt to the new reserve validation requirements.
387
	///
388
	/// # Behavior
389
	/// - **Before migration**: Permissive reserve handling (legacy behavior)
390
	/// - **After migration**: Strict DOT reserve checks enforced
391
	///
392
	/// The migration timing is controlled by [`AssetHubMigrationStartsAtRelayBlock`].
393
	type AssetHubMigrationStarted = AssetHubMigrationStarted;
394
}
395

            
396
impl cumulus_pallet_xcm::Config for Runtime {
397
	type RuntimeEvent = RuntimeEvent;
398
	type XcmExecutor = XcmExecutor;
399
}
400

            
401
impl cumulus_pallet_xcmp_queue::Config for Runtime {
402
	type RuntimeEvent = RuntimeEvent;
403
	type ChannelInfo = ParachainSystem;
404
	type VersionWrapper = PolkadotXcm;
405
	type XcmpQueue = TransformOrigin<MessageQueue, AggregateMessageOrigin, ParaId, ParaIdToSibling>;
406
	type MaxInboundSuspended = sp_core::ConstU32<1_000>;
407
	type ControllerOrigin = EnsureRoot<AccountId>;
408
	type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin;
409
	type WeightInfo = moonbeam_weights::cumulus_pallet_xcmp_queue::WeightInfo<Runtime>;
410
	type PriceForSiblingDelivery = polkadot_runtime_common::xcm_sender::NoPriceForMessageDelivery<
411
		cumulus_primitives_core::ParaId,
412
	>;
413
	type MaxActiveOutboundChannels = ConstU32<128>;
414
	// Most on-chain HRMP channels are configured to use 102400 bytes of max message size, so we
415
	// need to set the page size larger than that until we reduce the channel size on-chain.
416
	type MaxPageSize = MessageQueueHeapSize;
417
}
418

            
419
parameter_types! {
420
	pub const RelayOrigin: AggregateMessageOrigin = AggregateMessageOrigin::Parent;
421
}
422

            
423
parameter_types! {
424
	/// The amount of weight (if any) which should be provided to the message queue for
425
	/// servicing enqueued items.
426
	///
427
	/// This may be legitimately `None` in the case that you will call
428
	/// `ServiceQueues::service_queues` manually.
429
	pub MessageQueueServiceWeight: Weight =
430
		Perbill::from_percent(25) * RuntimeBlockWeights::get().max_block;
431
	/// The maximum number of stale pages (i.e. of overweight messages) allowed before culling
432
	/// can happen. Once there are more stale pages than this, then historical pages may be
433
	/// dropped, even if they contain unprocessed overweight messages.
434
	pub const MessageQueueMaxStale: u32 = 8;
435
	/// The size of the page; this implies the maximum message size which can be sent.
436
	///
437
	/// A good value depends on the expected message sizes, their weights, the weight that is
438
	/// available for processing them and the maximal needed message size. The maximal message
439
	/// size is slightly lower than this as defined by [`MaxMessageLenOf`].
440
	pub const MessageQueueHeapSize: u32 = 103 * 1024;
441
}
442

            
443
impl pallet_message_queue::Config for Runtime {
444
	type RuntimeEvent = RuntimeEvent;
445
	#[cfg(feature = "runtime-benchmarks")]
446
	type MessageProcessor = pallet_message_queue::mock_helpers::NoopMessageProcessor<
447
		cumulus_primitives_core::AggregateMessageOrigin,
448
	>;
449
	#[cfg(not(feature = "runtime-benchmarks"))]
450
	type MessageProcessor = pallet_ethereum_xcm::MessageProcessorWrapper<
451
		xcm_builder::ProcessXcmMessage<AggregateMessageOrigin, XcmExecutor, RuntimeCall>,
452
	>;
453
	type Size = u32;
454
	type HeapSize = MessageQueueHeapSize;
455
	type MaxStale = MessageQueueMaxStale;
456
	type ServiceWeight = MessageQueueServiceWeight;
457
	// The XCMP queue pallet is only ever able to handle the `Sibling(ParaId)` origin:
458
	type QueueChangeHandler = NarrowOriginToSibling<XcmpQueue>;
459
	// NarrowOriginToSibling calls XcmpQueue's is_paused if Origin is sibling. Allows all other origins
460
	type QueuePausedQuery = EmergencyParaXcm;
461
	type WeightInfo = moonbeam_weights::pallet_message_queue::WeightInfo<Runtime>;
462
	type IdleMaxServiceWeight = MessageQueueServiceWeight;
463
}
464

            
465
pub type FastAuthorizeUpgradeOrigin = EitherOfDiverse<
466
	EnsureRoot<AccountId>,
467
	pallet_collective::EnsureProportionAtLeast<AccountId, OpenTechCommitteeInstance, 5, 9>,
468
>;
469

            
470
pub type ResumeXcmOrigin = EitherOfDiverse<
471
	EnsureRoot<AccountId>,
472
	pallet_collective::EnsureProportionAtLeast<AccountId, OpenTechCommitteeInstance, 5, 9>,
473
>;
474

            
475
impl pallet_emergency_para_xcm::Config for Runtime {
476
	type RuntimeEvent = RuntimeEvent;
477
	type CheckAssociatedRelayNumber =
478
		cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases;
479
	type QueuePausedQuery = (MaintenanceMode, NarrowOriginToSibling<XcmpQueue>);
480
	type PausedThreshold = ConstU32<300>;
481
	type FastAuthorizeUpgradeOrigin = FastAuthorizeUpgradeOrigin;
482
	type PausedToNormalOrigin = ResumeXcmOrigin;
483
}
484

            
485
// Our AssetType. For now we only handle Xcm Assets
486
#[derive(
487
	Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo, DecodeWithMemTracking,
488
)]
489
pub enum AssetType {
490
	Xcm(xcm::v3::Location),
491
}
492
impl Default for AssetType {
493
	fn default() -> Self {
494
		Self::Xcm(xcm::v3::Location::here())
495
	}
496
}
497

            
498
impl From<xcm::v3::Location> for AssetType {
499
	fn from(location: xcm::v3::Location) -> Self {
500
		Self::Xcm(location)
501
	}
502
}
503

            
504
// This can be removed once we fully adopt xcm::v5 everywhere
505
impl TryFrom<Location> for AssetType {
506
	type Error = ();
507

            
508
54
	fn try_from(location: Location) -> Result<Self, Self::Error> {
509
54
		// Convert the V5 location to a V3 location
510
54
		match xcm::VersionedLocation::V5(location).into_version(xcm::v3::VERSION) {
511
54
			Ok(xcm::VersionedLocation::V3(loc)) => Ok(AssetType::Xcm(loc.into())),
512
			// Any other version or conversion error returns an error
513
			_ => Err(()),
514
		}
515
54
	}
516
}
517

            
518
impl Into<Option<xcm::v3::Location>> for AssetType {
519
	fn into(self) -> Option<xcm::v3::Location> {
520
		match self {
521
			Self::Xcm(location) => Some(location),
522
		}
523
	}
524
}
525

            
526
impl Into<Option<Location>> for AssetType {
527
	fn into(self) -> Option<Location> {
528
		match self {
529
			Self::Xcm(location) => {
530
				let versioned = xcm::VersionedLocation::V3(location);
531
				match versioned.into_version(xcm::latest::VERSION) {
532
					Ok(xcm::VersionedLocation::V5(loc)) => Some(loc),
533
					_ => None,
534
				}
535
			}
536
		}
537
	}
538
}
539

            
540
// Implementation on how to retrieve the AssetId from an AssetType
541
// We simply hash the AssetType and take the lowest 128 bits
542
impl From<AssetType> for AssetId {
543
186
	fn from(asset: AssetType) -> AssetId {
544
186
		match asset {
545
186
			AssetType::Xcm(id) => {
546
186
				let mut result: [u8; 16] = [0u8; 16];
547
186
				let hash: H256 = id.using_encoded(<Runtime as frame_system::Config>::Hashing::hash);
548
186
				result.copy_from_slice(&hash.as_fixed_bytes()[0..16]);
549
186
				u128::from_le_bytes(result)
550
186
			}
551
186
		}
552
186
	}
553
}
554

            
555
// Our currencyId. We distinguish for now between SelfReserve, and Others, defined by their Id.
556
#[derive(
557
18
	Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo, DecodeWithMemTracking,
558
)]
559
pub enum CurrencyId {
560
	// Our native token
561
	SelfReserve,
562
	// Assets representing other chains native tokens
563
	ForeignAsset(AssetId),
564
	// Erc20 token
565
	Erc20 { contract_address: H160 },
566
}
567

            
568
impl AccountIdToCurrencyId<AccountId, CurrencyId> for Runtime {
569
	fn account_to_currency_id(account: AccountId) -> Option<CurrencyId> {
570
		Some(match account {
571
			// the self-reserve currency is identified by the pallet-balances address
572
			a if a == H160::from_low_u64_be(2050).into() => CurrencyId::SelfReserve,
573
			// the rest of the currencies, by their corresponding erc20 address
574
			_ => match Runtime::account_to_asset_id(account) {
575
				// We distinguish by prefix, and depending on it we create either
576
				// Foreign or Local
577
				Some((_prefix, asset_id)) => CurrencyId::ForeignAsset(asset_id),
578
				// If no known prefix is identified, we consider that it's a "real" erc20 token
579
				// (i.e. managed by a real smart contract)
580
				None => CurrencyId::Erc20 {
581
					contract_address: account.into(),
582
				},
583
			},
584
		})
585
	}
586
}
587
// How to convert from CurrencyId to Location
588
pub struct CurrencyIdToLocation<AssetXConverter>(sp_std::marker::PhantomData<AssetXConverter>);
589
impl<AssetXConverter> sp_runtime::traits::Convert<CurrencyId, Option<Location>>
590
	for CurrencyIdToLocation<AssetXConverter>
591
where
592
	AssetXConverter: MaybeEquivalence<Location, AssetId>,
593
{
594
9
	fn convert(currency: CurrencyId) -> Option<Location> {
595
9
		match currency {
596
			CurrencyId::SelfReserve => {
597
1
				let multi: Location = SelfReserve::get();
598
1
				Some(multi)
599
			}
600
8
			CurrencyId::ForeignAsset(asset) => AssetXConverter::convert_back(&asset),
601
			CurrencyId::Erc20 { contract_address } => {
602
				let mut location = Erc20XcmBridgePalletLocation::get();
603
				location
604
					.push_interior(Junction::AccountKey20 {
605
						key: contract_address.0,
606
						network: None,
607
					})
608
					.ok();
609
				Some(location)
610
			}
611
		}
612
9
	}
613
}
614

            
615
parameter_types! {
616
	pub const BaseXcmWeight: Weight = Weight::from_parts(200_000_000u64, 0);
617
	pub const MaxAssetsForTransfer: usize = 2;
618

            
619
	// This is how we are going to detect whether the asset is a Reserve asset
620
	// This however is the chain part only
621
	pub SelfLocation: Location = Location::here();
622
	// We need this to be able to catch when someone is trying to execute a non-
623
	// cross-chain transfer in xtokens through the absolute path way
624
	pub SelfLocationAbsolute: Location = Location {
625
		parents:1,
626
		interior: [
627
			Parachain(ParachainInfo::parachain_id().into())
628
		].into()
629
	};
630
}
631

            
632
// 1 DOT should be enough
633
parameter_types! {
634
	pub MaxHrmpRelayFee: Asset = (Location::parent(), 1_000_000_000_000u128).into();
635
}
636

            
637
// For now we only allow to transact in the relay, although this might change in the future
638
// Transactors just defines the chains in which we allow transactions to be issued through
639
// xcm
640
#[derive(
641
	Clone,
642
	Eq,
643
	Debug,
644
	PartialEq,
645
	Ord,
646
	PartialOrd,
647
	Encode,
648
	Decode,
649
12
	TypeInfo,
650
	DecodeWithMemTracking,
651
	serde::Serialize,
652
	serde::Deserialize,
653
)]
654
pub enum Transactors {
655
	Relay,
656
	AssetHub,
657
}
658

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

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

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

            
691
impl XcmTransact for Transactors {
692
12
	fn destination(self) -> Location {
693
12
		match self {
694
12
			Transactors::Relay => Location::parent(),
695
			Transactors::AssetHub => Location {
696
				parents: 1,
697
				interior: [Parachain(1000)].into(),
698
			},
699
		}
700
12
	}
701
}
702

            
703
impl xcm_primitives::RelayChainTransactor for Transactors {
704
1
	fn relay() -> Self {
705
1
		Transactors::Relay
706
1
	}
707
}
708

            
709
impl xcm_primitives::AssetHubTransactor for Transactors {
710
1
	fn asset_hub() -> Self {
711
1
		Transactors::AssetHub
712
1
	}
713
}
714

            
715
pub type DerivativeAddressRegistrationOrigin =
716
	EitherOfDiverse<EnsureRoot<AccountId>, governance::custom_origins::GeneralAdmin>;
717

            
718
impl pallet_xcm_transactor::Config for Runtime {
719
	type RuntimeEvent = RuntimeEvent;
720
	type Balance = Balance;
721
	type Transactor = Transactors;
722
	type DerivativeAddressRegistrationOrigin = DerivativeAddressRegistrationOrigin;
723
	type SovereignAccountDispatcherOrigin = EnsureRoot<AccountId>;
724
	type CurrencyId = CurrencyId;
725
	type AccountIdToLocation = AccountIdToLocation<AccountId>;
726
	type CurrencyIdToLocation = CurrencyIdToLocation<(EvmForeignAssets,)>;
727
	type XcmSender = XcmRouter;
728
	type SelfLocation = SelfLocation;
729
	type Weigher = XcmWeigher;
730
	type UniversalLocation = UniversalLocation;
731
	type BaseXcmWeight = BaseXcmWeight;
732
	type AssetTransactor = AssetTransactors;
733
	type ReserveProvider = AbsoluteAndRelativeReserve<SelfLocationAbsolute>;
734
	type WeightInfo = moonbeam_weights::pallet_xcm_transactor::WeightInfo<Runtime>;
735
	type HrmpManipulatorOrigin = GeneralAdminOrRoot;
736
	type HrmpOpenOrigin = FastGeneralAdminOrRoot;
737
	type MaxHrmpFee = xcm_builder::Case<MaxHrmpRelayFee>;
738
}
739

            
740
parameter_types! {
741
	// This is the relative view of erc20 assets.
742
	// Identified by this prefix + AccountKey20(contractAddress)
743
	// We use the RELATIVE multilocation
744
	pub Erc20XcmBridgePalletLocation: Location = Location {
745
		parents:0,
746
		interior: [
747
			PalletInstance(<Erc20XcmBridge as PalletInfoAccess>::index() as u8)
748
		].into()
749
	};
750

            
751
	// To be able to support almost all erc20 implementations,
752
	// we provide a sufficiently hight gas limit.
753
	pub Erc20XcmBridgeTransferGasLimit: u64 = 400_000;
754
}
755

            
756
impl pallet_erc20_xcm_bridge::Config for Runtime {
757
	type AccountIdConverter = LocationToH160;
758
	type Erc20MultilocationPrefix = Erc20XcmBridgePalletLocation;
759
	type Erc20TransferGasLimit = Erc20XcmBridgeTransferGasLimit;
760
	type EvmRunner = EvmRunnerPrecompileOrEthXcm<MoonbeamCall, Self>;
761
}
762

            
763
pub struct AccountIdToH160;
764
impl sp_runtime::traits::Convert<AccountId, H160> for AccountIdToH160 {
765
336
	fn convert(account_id: AccountId) -> H160 {
766
336
		account_id.into()
767
336
	}
768
}
769

            
770
pub type ForeignAssetManagerOrigin = EitherOf<
771
	MapSuccessToXcm<EnsureXcm<AllowSiblingParachains>>,
772
	MapSuccessToGovernance<
773
		EitherOf<
774
			EnsureRoot<AccountId>,
775
			EitherOf<
776
				pallet_collective::EnsureProportionMoreThan<
777
					AccountId,
778
					OpenTechCommitteeInstance,
779
					5,
780
					9,
781
				>,
782
				EitherOf<
783
					governance::custom_origins::FastGeneralAdmin,
784
					governance::custom_origins::GeneralAdmin,
785
				>,
786
			>,
787
		>,
788
	>,
789
>;
790

            
791
impl pallet_moonbeam_foreign_assets::Config for Runtime {
792
	type AccountIdToH160 = AccountIdToH160;
793
	type AssetIdFilter = Everything;
794
	type EvmRunner = EvmRunnerPrecompileOrEthXcm<MoonbeamCall, Self>;
795
	type ConvertLocation =
796
		SiblingParachainConvertsVia<polkadot_parachain::primitives::Sibling, AccountId>;
797
	type ForeignAssetCreatorOrigin = ForeignAssetManagerOrigin;
798
	type ForeignAssetFreezerOrigin = ForeignAssetManagerOrigin;
799
	type ForeignAssetModifierOrigin = ForeignAssetManagerOrigin;
800
	type ForeignAssetUnfreezerOrigin = ForeignAssetManagerOrigin;
801
	type OnForeignAssetCreated = ();
802
	type MaxForeignAssets = ConstU32<256>;
803
	type RuntimeEvent = RuntimeEvent;
804
	type WeightInfo = moonbeam_weights::pallet_moonbeam_foreign_assets::WeightInfo<Runtime>;
805
	type XcmLocationToH160 = LocationToH160;
806
	type ForeignAssetCreationDeposit = dynamic_params::xcm_config::ForeignAssetCreationDeposit;
807
	type Balance = Balance;
808
	type Currency = Balances;
809
}
810

            
811
pub struct AssetFeesFilter;
812
impl frame_support::traits::Contains<Location> for AssetFeesFilter {
813
42
	fn contains(location: &Location) -> bool {
814
42
		location.parent_count() > 0
815
42
			&& location.first_interior() != Erc20XcmBridgePalletLocation::get().first_interior()
816
42
	}
817
}
818

            
819
pub type AddAndEditSupportedAssetOrigin = EitherOfDiverse<
820
	EnsureRoot<AccountId>,
821
	EitherOfDiverse<
822
		pallet_collective::EnsureProportionMoreThan<AccountId, OpenTechCommitteeInstance, 5, 9>,
823
		EitherOf<
824
			governance::custom_origins::GeneralAdmin,
825
			governance::custom_origins::FastGeneralAdmin,
826
		>,
827
	>,
828
>;
829

            
830
pub type RemoveSupportedAssetOrigin = EitherOfDiverse<
831
	EnsureRoot<AccountId>,
832
	pallet_collective::EnsureProportionMoreThan<AccountId, OpenTechCommitteeInstance, 5, 9>,
833
>;
834

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

            
854
#[cfg(feature = "runtime-benchmarks")]
855
mod testing {
856
	use super::*;
857

            
858
	/// This From exists for benchmarking purposes. It has the potential side-effect of calling
859
	/// EvmForeignAssets::set_asset() and should NOT be used in any production code.
860
	impl From<Location> for CurrencyId {
861
		fn from(location: Location) -> CurrencyId {
862
			use sp_runtime::traits::MaybeEquivalence;
863

            
864
			// If it does not exist, for benchmarking purposes, we create the association
865
			let asset_id = if let Some(asset_id) = EvmForeignAssets::convert(&location) {
866
				asset_id
867
			} else {
868
				// Generate asset ID from location hash (similar to old AssetManager approach)
869
				let hash: H256 =
870
					location.using_encoded(<Runtime as frame_system::Config>::Hashing::hash);
871
				let mut result: [u8; 16] = [0u8; 16];
872
				result.copy_from_slice(&hash.as_fixed_bytes()[0..16]);
873
				let asset_id = u128::from_le_bytes(result);
874

            
875
				EvmForeignAssets::set_asset(location, asset_id);
876
				asset_id
877
			};
878

            
879
			CurrencyId::ForeignAsset(asset_id)
880
		}
881
	}
882
}
883

            
884
#[cfg(test)]
885
mod tests {
886
	use super::AssetHubMigrationStartsAtRelayBlock;
887

            
888
	#[test]
889
1
	fn check_type_parameter_key() {
890
1
		let implicit_key = AssetHubMigrationStartsAtRelayBlock::key();
891
1
		let explicit_key = sp_core::twox_128(b":AssetHubMigrationStartsAtRelayBlock:");
892
1
		assert_eq!(implicit_key, explicit_key);
893
1
	}
894
}