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
	ExternalConsensusLocationsConverterFor, FungibleAdapter as XcmCurrencyAdapter,
46
	HashedDescription, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative,
47
	SiblingParachainConvertsVia, SignedAccountKey20AsNative, SovereignSignedViaLocation,
48
	TakeWeightCredit, TrailingSetTopicAsId, 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, XcmTransact,
68
};
69

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

            
82
parameter_types! {
83
	// The network Id of the relay
84
	pub const RelayNetwork: NetworkId = NetworkId::Polkadot;
85
	// The relay chain Origin type
86
	pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into();
87
	pub UniversalLocation: InteriorLocation =
88
		[GlobalConsensus(RelayNetwork::get()), Parachain(ParachainInfo::parachain_id().into())].into();
89
	// Self Reserve location, defines the multilocation identifiying the self-reserve currency
90
	// This is used to match it also against our Balances pallet when we receive such
91
	// a Location: (Self Balances pallet index)
92
	// We use the RELATIVE multilocation
93
	pub SelfReserve: Location = Location {
94
		parents:0,
95
		interior: [
96
			PalletInstance(<Balances as PalletInfoAccess>::index() as u8)
97
		].into()
98
	};
99
}
100

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

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

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

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

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

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

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

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

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

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

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

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

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

            
243
moonbeam_runtime_common::impl_evm_runner_precompile_or_eth_xcm!();
244

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

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

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

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

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

            
315
impl pallet_xcm::Config for Runtime {
316
	type RuntimeEvent = RuntimeEvent;
317
	type SendXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
318
	type XcmRouter = XcmRouter;
319
	type ExecuteXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
320
	type XcmExecuteFilter = Nothing;
321
	type XcmExecutor = XcmExecutor;
322
	type XcmTeleportFilter = Nothing;
323
	type XcmReserveTransferFilter = Everything;
324
	type Weigher = XcmWeigher;
325
	type UniversalLocation = UniversalLocation;
326
	type RuntimeOrigin = RuntimeOrigin;
327
	type RuntimeCall = RuntimeCall;
328
	const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
329
	type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
330
	type Currency = Balances;
331
	type CurrencyMatcher = ();
332
	type TrustedLockers = ();
333
	type SovereignAccountOf = LocationToAccountId;
334
	type MaxLockers = ConstU32<8>;
335
	type MaxRemoteLockConsumers = ConstU32<0>;
336
	type RemoteLockConsumerIdentifier = ();
337
	type WeightInfo = moonbeam_weights::pallet_xcm::WeightInfo<Runtime>;
338
	type AdminOrigin = EnsureRoot<AccountId>;
339
	type AuthorizedAliasConsideration = Disabled;
340
}
341

            
342
impl cumulus_pallet_xcm::Config for Runtime {
343
	type RuntimeEvent = RuntimeEvent;
344
	type XcmExecutor = XcmExecutor;
345
}
346

            
347
impl cumulus_pallet_xcmp_queue::Config for Runtime {
348
	type RuntimeEvent = RuntimeEvent;
349
	type ChannelInfo = ParachainSystem;
350
	type VersionWrapper = PolkadotXcm;
351
	type XcmpQueue = TransformOrigin<MessageQueue, AggregateMessageOrigin, ParaId, ParaIdToSibling>;
352
	type MaxInboundSuspended = sp_core::ConstU32<1_000>;
353
	type ControllerOrigin = EnsureRoot<AccountId>;
354
	type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin;
355
	type WeightInfo = moonbeam_weights::cumulus_pallet_xcmp_queue::WeightInfo<Runtime>;
356
	type PriceForSiblingDelivery = polkadot_runtime_common::xcm_sender::NoPriceForMessageDelivery<
357
		cumulus_primitives_core::ParaId,
358
	>;
359
	type MaxActiveOutboundChannels = ConstU32<128>;
360
	// Most on-chain HRMP channels are configured to use 102400 bytes of max message size, so we
361
	// need to set the page size larger than that until we reduce the channel size on-chain.
362
	type MaxPageSize = MessageQueueHeapSize;
363
}
364

            
365
parameter_types! {
366
	pub const RelayOrigin: AggregateMessageOrigin = AggregateMessageOrigin::Parent;
367
}
368

            
369
parameter_types! {
370
	/// The amount of weight (if any) which should be provided to the message queue for
371
	/// servicing enqueued items.
372
	///
373
	/// This may be legitimately `None` in the case that you will call
374
	/// `ServiceQueues::service_queues` manually.
375
	pub MessageQueueServiceWeight: Weight =
376
		Perbill::from_percent(25) * RuntimeBlockWeights::get().max_block;
377
	/// The maximum number of stale pages (i.e. of overweight messages) allowed before culling
378
	/// can happen. Once there are more stale pages than this, then historical pages may be
379
	/// dropped, even if they contain unprocessed overweight messages.
380
	pub const MessageQueueMaxStale: u32 = 8;
381
	/// The size of the page; this implies the maximum message size which can be sent.
382
	///
383
	/// A good value depends on the expected message sizes, their weights, the weight that is
384
	/// available for processing them and the maximal needed message size. The maximal message
385
	/// size is slightly lower than this as defined by [`MaxMessageLenOf`].
386
	pub const MessageQueueHeapSize: u32 = 103 * 1024;
387
}
388

            
389
impl pallet_message_queue::Config for Runtime {
390
	type RuntimeEvent = RuntimeEvent;
391
	#[cfg(feature = "runtime-benchmarks")]
392
	type MessageProcessor = pallet_message_queue::mock_helpers::NoopMessageProcessor<
393
		cumulus_primitives_core::AggregateMessageOrigin,
394
	>;
395
	#[cfg(not(feature = "runtime-benchmarks"))]
396
	type MessageProcessor = pallet_ethereum_xcm::MessageProcessorWrapper<
397
		xcm_builder::ProcessXcmMessage<AggregateMessageOrigin, XcmExecutor, RuntimeCall>,
398
	>;
399
	type Size = u32;
400
	type HeapSize = MessageQueueHeapSize;
401
	type MaxStale = MessageQueueMaxStale;
402
	type ServiceWeight = MessageQueueServiceWeight;
403
	// The XCMP queue pallet is only ever able to handle the `Sibling(ParaId)` origin:
404
	type QueueChangeHandler = NarrowOriginToSibling<XcmpQueue>;
405
	// NarrowOriginToSibling calls XcmpQueue's is_paused if Origin is sibling. Allows all other origins
406
	type QueuePausedQuery = EmergencyParaXcm;
407
	type WeightInfo = moonbeam_weights::pallet_message_queue::WeightInfo<Runtime>;
408
	type IdleMaxServiceWeight = MessageQueueServiceWeight;
409
}
410

            
411
pub type FastAuthorizeUpgradeOrigin = EitherOfDiverse<
412
	EnsureRoot<AccountId>,
413
	pallet_collective::EnsureProportionAtLeast<AccountId, OpenTechCommitteeInstance, 5, 9>,
414
>;
415

            
416
pub type ResumeXcmOrigin = EitherOfDiverse<
417
	EnsureRoot<AccountId>,
418
	pallet_collective::EnsureProportionAtLeast<AccountId, OpenTechCommitteeInstance, 5, 9>,
419
>;
420

            
421
impl pallet_emergency_para_xcm::Config for Runtime {
422
	type CheckAssociatedRelayNumber =
423
		cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases;
424
	type QueuePausedQuery = (MaintenanceMode, NarrowOriginToSibling<XcmpQueue>);
425
	type PausedThreshold = ConstU32<300>;
426
	type FastAuthorizeUpgradeOrigin = FastAuthorizeUpgradeOrigin;
427
	type PausedToNormalOrigin = ResumeXcmOrigin;
428
}
429

            
430
// Our AssetType. For now we only handle Xcm Assets
431
#[derive(
432
	Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo, DecodeWithMemTracking,
433
)]
434
pub enum AssetType {
435
	Xcm(xcm::v3::Location),
436
}
437
impl Default for AssetType {
438
	fn default() -> Self {
439
		Self::Xcm(xcm::v3::Location::here())
440
	}
441
}
442

            
443
impl From<xcm::v3::Location> for AssetType {
444
	fn from(location: xcm::v3::Location) -> Self {
445
		Self::Xcm(location)
446
	}
447
}
448

            
449
// This can be removed once we fully adopt xcm::v5 everywhere
450
impl TryFrom<Location> for AssetType {
451
	type Error = ();
452

            
453
54
	fn try_from(location: Location) -> Result<Self, Self::Error> {
454
		// Convert the V5 location to a V3 location
455
54
		match xcm::VersionedLocation::V5(location).into_version(xcm::v3::VERSION) {
456
54
			Ok(xcm::VersionedLocation::V3(loc)) => Ok(AssetType::Xcm(loc.into())),
457
			// Any other version or conversion error returns an error
458
			_ => Err(()),
459
		}
460
54
	}
461
}
462

            
463
impl Into<Option<xcm::v3::Location>> for AssetType {
464
	fn into(self) -> Option<xcm::v3::Location> {
465
		match self {
466
			Self::Xcm(location) => Some(location),
467
		}
468
	}
469
}
470

            
471
impl Into<Option<Location>> for AssetType {
472
	fn into(self) -> Option<Location> {
473
		match self {
474
			Self::Xcm(location) => {
475
				let versioned = xcm::VersionedLocation::V3(location);
476
				match versioned.into_version(xcm::latest::VERSION) {
477
					Ok(xcm::VersionedLocation::V5(loc)) => Some(loc),
478
					_ => None,
479
				}
480
			}
481
		}
482
	}
483
}
484

            
485
// Implementation on how to retrieve the AssetId from an AssetType
486
// We simply hash the AssetType and take the lowest 128 bits
487
impl From<AssetType> for AssetId {
488
186
	fn from(asset: AssetType) -> AssetId {
489
186
		match asset {
490
186
			AssetType::Xcm(id) => {
491
186
				let mut result: [u8; 16] = [0u8; 16];
492
186
				let hash: H256 = id.using_encoded(<Runtime as frame_system::Config>::Hashing::hash);
493
186
				result.copy_from_slice(&hash.as_fixed_bytes()[0..16]);
494
186
				u128::from_le_bytes(result)
495
			}
496
		}
497
186
	}
498
}
499

            
500
// Our currencyId. We distinguish for now between SelfReserve, and Others, defined by their Id.
501
#[derive(
502
	Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo, DecodeWithMemTracking,
503
)]
504
pub enum CurrencyId {
505
	// Our native token
506
	SelfReserve,
507
	// Assets representing other chains native tokens
508
	ForeignAsset(AssetId),
509
	// Erc20 token
510
	Erc20 { contract_address: H160 },
511
}
512

            
513
impl AccountIdToCurrencyId<AccountId, CurrencyId> for Runtime {
514
	fn account_to_currency_id(account: AccountId) -> Option<CurrencyId> {
515
		Some(match account {
516
			// the self-reserve currency is identified by the pallet-balances address
517
			a if a == H160::from_low_u64_be(2050).into() => CurrencyId::SelfReserve,
518
			// the rest of the currencies, by their corresponding erc20 address
519
			_ => match Runtime::account_to_asset_id(account) {
520
				// We distinguish by prefix, and depending on it we create either
521
				// Foreign or Local
522
				Some((_prefix, asset_id)) => CurrencyId::ForeignAsset(asset_id),
523
				// If no known prefix is identified, we consider that it's a "real" erc20 token
524
				// (i.e. managed by a real smart contract)
525
				None => CurrencyId::Erc20 {
526
					contract_address: account.into(),
527
				},
528
			},
529
		})
530
	}
531
}
532
// How to convert from CurrencyId to Location
533
pub struct CurrencyIdToLocation<AssetXConverter>(sp_std::marker::PhantomData<AssetXConverter>);
534
impl<AssetXConverter> sp_runtime::traits::Convert<CurrencyId, Option<Location>>
535
	for CurrencyIdToLocation<AssetXConverter>
536
where
537
	AssetXConverter: MaybeEquivalence<Location, AssetId>,
538
{
539
5
	fn convert(currency: CurrencyId) -> Option<Location> {
540
5
		match currency {
541
			CurrencyId::SelfReserve => {
542
1
				let multi: Location = SelfReserve::get();
543
1
				Some(multi)
544
			}
545
4
			CurrencyId::ForeignAsset(asset) => AssetXConverter::convert_back(&asset),
546
			CurrencyId::Erc20 { contract_address } => {
547
				let mut location = Erc20XcmBridgePalletLocation::get();
548
				location
549
					.push_interior(Junction::AccountKey20 {
550
						key: contract_address.0,
551
						network: None,
552
					})
553
					.ok();
554
				Some(location)
555
			}
556
		}
557
5
	}
558
}
559

            
560
parameter_types! {
561
	pub const BaseXcmWeight: Weight = Weight::from_parts(200_000_000u64, 0);
562
	pub const MaxAssetsForTransfer: usize = 2;
563

            
564
	// This is how we are going to detect whether the asset is a Reserve asset
565
	// This however is the chain part only
566
	pub SelfLocation: Location = Location::here();
567
	// We need this to be able to catch when someone is trying to execute a non-
568
	// cross-chain transfer in xtokens through the absolute path way
569
	pub SelfLocationAbsolute: Location = Location {
570
		parents:1,
571
		interior: [
572
			Parachain(ParachainInfo::parachain_id().into())
573
		].into()
574
	};
575
}
576

            
577
// 1 DOT should be enough
578
parameter_types! {
579
	pub MaxHrmpRelayFee: Asset = (Location::parent(), 1_000_000_000_000u128).into();
580
}
581

            
582
// For now we only allow to transact in the relay, although this might change in the future
583
// Transactors just defines the chains in which we allow transactions to be issued through
584
// xcm
585
#[derive(
586
	Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo, DecodeWithMemTracking,
587
)]
588
pub enum Transactors {
589
	Relay,
590
	AssetHub,
591
}
592

            
593
// Default for benchmarking
594
#[cfg(feature = "runtime-benchmarks")]
595
impl Default for Transactors {
596
	fn default() -> Self {
597
		Transactors::Relay
598
	}
599
}
600

            
601
impl TryFrom<u8> for Transactors {
602
	type Error = ();
603
	fn try_from(value: u8) -> Result<Self, Self::Error> {
604
		match value {
605
			0u8 => Ok(Transactors::Relay),
606
			1u8 => Ok(Transactors::AssetHub),
607
			_ => Err(()),
608
		}
609
	}
610
}
611

            
612
impl XcmTransact for Transactors {
613
12
	fn destination(self) -> Location {
614
12
		match self {
615
12
			Transactors::Relay => RelayLocation::get(),
616
			Transactors::AssetHub => AssetHubLocation::get(),
617
		}
618
12
	}
619

            
620
12
	fn utility_pallet_index(&self) -> u8 {
621
12
		match self {
622
12
			Transactors::Relay => pallet_xcm_transactor::RelayIndices::<Runtime>::get().utility,
623
			Transactors::AssetHub => pallet_xcm_transactor::ASSET_HUB_UTILITY_PALLET_INDEX,
624
		}
625
12
	}
626

            
627
	fn staking_pallet_index(&self) -> u8 {
628
		match self {
629
			Transactors::Relay => pallet_xcm_transactor::RelayIndices::<Runtime>::get().staking,
630
			Transactors::AssetHub => pallet_xcm_transactor::ASSET_HUB_STAKING_PALLET_INDEX,
631
		}
632
	}
633
}
634

            
635
pub type DerivativeAddressRegistrationOrigin =
636
	EitherOfDiverse<EnsureRoot<AccountId>, governance::custom_origins::GeneralAdmin>;
637

            
638
impl pallet_xcm_transactor::Config for Runtime {
639
	type Balance = Balance;
640
	type Transactor = Transactors;
641
	type DerivativeAddressRegistrationOrigin = DerivativeAddressRegistrationOrigin;
642
	type SovereignAccountDispatcherOrigin = EnsureRoot<AccountId>;
643
	type CurrencyId = CurrencyId;
644
	type AccountIdToLocation = AccountIdToLocation<AccountId>;
645
	type CurrencyIdToLocation = CurrencyIdToLocation<(EvmForeignAssets,)>;
646
	type XcmSender = XcmRouter;
647
	type SelfLocation = SelfLocation;
648
	type Weigher = XcmWeigher;
649
	type UniversalLocation = UniversalLocation;
650
	type BaseXcmWeight = BaseXcmWeight;
651
	type AssetTransactor = AssetTransactors;
652
	type ReserveProvider = AbsoluteAndRelativeReserve<SelfLocationAbsolute>;
653
	type WeightInfo = moonbeam_weights::pallet_xcm_transactor::WeightInfo<Runtime>;
654
	type HrmpManipulatorOrigin = GeneralAdminOrRoot;
655
	type HrmpOpenOrigin = FastGeneralAdminOrRoot;
656
	type MaxHrmpFee = xcm_builder::Case<MaxHrmpRelayFee>;
657
}
658

            
659
parameter_types! {
660
	// This is the relative view of erc20 assets.
661
	// Identified by this prefix + AccountKey20(contractAddress)
662
	// We use the RELATIVE multilocation
663
	pub Erc20XcmBridgePalletLocation: Location = Location {
664
		parents:0,
665
		interior: [
666
			PalletInstance(<Erc20XcmBridge as PalletInfoAccess>::index() as u8)
667
		].into()
668
	};
669

            
670
	// To be able to support almost all erc20 implementations,
671
	// we provide a sufficiently hight gas limit.
672
	pub Erc20XcmBridgeTransferGasLimit: u64 = 400_000;
673
}
674

            
675
impl pallet_erc20_xcm_bridge::Config for Runtime {
676
	type AccountIdConverter = LocationToH160;
677
	type Erc20MultilocationPrefix = Erc20XcmBridgePalletLocation;
678
	type Erc20TransferGasLimit = Erc20XcmBridgeTransferGasLimit;
679
	type EvmRunner = EvmRunnerPrecompileOrEthXcm<MoonbeamCall, Self>;
680
}
681

            
682
pub struct AccountIdToH160;
683
impl sp_runtime::traits::Convert<AccountId, H160> for AccountIdToH160 {
684
234
	fn convert(account_id: AccountId) -> H160 {
685
234
		account_id.into()
686
234
	}
687
}
688

            
689
pub type ForeignAssetManagerOrigin = EitherOf<
690
	MapSuccessToXcm<EnsureXcm<AllowSiblingParachains>>,
691
	MapSuccessToGovernance<
692
		EitherOf<
693
			EnsureRoot<AccountId>,
694
			EitherOf<
695
				pallet_collective::EnsureProportionMoreThan<
696
					AccountId,
697
					OpenTechCommitteeInstance,
698
					5,
699
					9,
700
				>,
701
				EitherOf<
702
					governance::custom_origins::FastGeneralAdmin,
703
					governance::custom_origins::GeneralAdmin,
704
				>,
705
			>,
706
		>,
707
	>,
708
>;
709

            
710
impl pallet_moonbeam_foreign_assets::Config for Runtime {
711
	type AccountIdToH160 = AccountIdToH160;
712
	type AssetIdFilter = Everything;
713
	type EvmRunner = EvmRunnerPrecompileOrEthXcm<MoonbeamCall, Self>;
714
	type ConvertLocation =
715
		SiblingParachainConvertsVia<polkadot_parachain::primitives::Sibling, AccountId>;
716
	type ForeignAssetCreatorOrigin = ForeignAssetManagerOrigin;
717
	type ForeignAssetFreezerOrigin = ForeignAssetManagerOrigin;
718
	type ForeignAssetModifierOrigin = ForeignAssetManagerOrigin;
719
	type ForeignAssetUnfreezerOrigin = ForeignAssetManagerOrigin;
720
	type OnForeignAssetCreated = ();
721
	type MaxForeignAssets = ConstU32<256>;
722
	type WeightInfo = moonbeam_weights::pallet_moonbeam_foreign_assets::WeightInfo<Runtime>;
723
	type XcmLocationToH160 = LocationToH160;
724
	type ForeignAssetCreationDeposit = dynamic_params::xcm_config::ForeignAssetCreationDeposit;
725
	type Balance = Balance;
726
	type Currency = Balances;
727
}
728

            
729
pub struct AssetFeesFilter;
730
impl frame_support::traits::Contains<Location> for AssetFeesFilter {
731
30
	fn contains(location: &Location) -> bool {
732
30
		location.parent_count() > 0
733
30
			&& location.first_interior() != Erc20XcmBridgePalletLocation::get().first_interior()
734
30
	}
735
}
736

            
737
pub type AddAndEditSupportedAssetOrigin = EitherOfDiverse<
738
	EnsureRoot<AccountId>,
739
	EitherOfDiverse<
740
		pallet_collective::EnsureProportionMoreThan<AccountId, OpenTechCommitteeInstance, 5, 9>,
741
		EitherOf<
742
			governance::custom_origins::GeneralAdmin,
743
			governance::custom_origins::FastGeneralAdmin,
744
		>,
745
	>,
746
>;
747

            
748
pub type RemoveSupportedAssetOrigin = EitherOfDiverse<
749
	EnsureRoot<AccountId>,
750
	pallet_collective::EnsureProportionMoreThan<AccountId, OpenTechCommitteeInstance, 5, 9>,
751
>;
752

            
753
impl pallet_xcm_weight_trader::Config for Runtime {
754
	type AccountIdToLocation = AccountIdToLocation<AccountId>;
755
	type AddSupportedAssetOrigin = AddAndEditSupportedAssetOrigin;
756
	type AssetLocationFilter = AssetFeesFilter;
757
	type AssetTransactor = AssetTransactors;
758
	type Balance = Balance;
759
	type EditSupportedAssetOrigin = AddAndEditSupportedAssetOrigin;
760
	type NativeLocation = SelfReserve;
761
	type PauseSupportedAssetOrigin = AddAndEditSupportedAssetOrigin;
762
	type ResumeSupportedAssetOrigin = AddAndEditSupportedAssetOrigin;
763
	type RemoveSupportedAssetOrigin = RemoveSupportedAssetOrigin;
764
	type WeightInfo = moonbeam_weights::pallet_xcm_weight_trader::WeightInfo<Runtime>;
765
	type WeightToFee = <Runtime as pallet_transaction_payment::Config>::WeightToFee;
766
	type XcmFeesAccount = XcmFeesAccount;
767
	#[cfg(feature = "runtime-benchmarks")]
768
	type NotFilteredLocation = RelayLocation;
769
}
770

            
771
#[cfg(feature = "runtime-benchmarks")]
772
mod testing {
773
	use super::*;
774

            
775
	/// This From exists for benchmarking purposes. It has the potential side-effect of calling
776
	/// EvmForeignAssets::set_asset() and should NOT be used in any production code.
777
	impl From<Location> for CurrencyId {
778
		fn from(location: Location) -> CurrencyId {
779
			use sp_runtime::traits::MaybeEquivalence;
780

            
781
			// If it does not exist, for benchmarking purposes, we create the association
782
			let asset_id = if let Some(asset_id) = EvmForeignAssets::convert(&location) {
783
				asset_id
784
			} else {
785
				// Generate asset ID from location hash (similar to old AssetManager approach)
786
				let hash: H256 =
787
					location.using_encoded(<Runtime as frame_system::Config>::Hashing::hash);
788
				let mut result: [u8; 16] = [0u8; 16];
789
				result.copy_from_slice(&hash.as_fixed_bytes()[0..16]);
790
				let asset_id = u128::from_le_bytes(result);
791

            
792
				EvmForeignAssets::set_asset(location, asset_id);
793
				asset_id
794
			};
795

            
796
			CurrencyId::ForeignAsset(asset_id)
797
		}
798
	}
799
}