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, AssetManager, Balance, Balances,
22
	BridgeXcmOverMoonbeam, EmergencyParaXcm, Erc20XcmBridge, EvmForeignAssets, MaintenanceMode,
23
	MessageQueue, OpenTechCommitteeInstance, ParachainInfo, ParachainSystem, Perbill, PolkadotXcm,
24
	Runtime, RuntimeBlockWeights, RuntimeCall, RuntimeEvent, RuntimeOrigin, Treasury, XcmpQueue,
25
};
26

            
27
use super::moonriver_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, ConvertedConcreteId, DescribeAllTerminal, DescribeFamily,
45
	EnsureXcmOrigin, FungibleAdapter as XcmCurrencyAdapter, FungiblesAdapter,
46
	GlobalConsensusParachainConvertsFor, HashedDescription, NoChecking, ParentIsPreset,
47
	RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia,
48
	SignedAccountKey20AsNative, SovereignSignedViaLocation, TakeWeightCredit, TrailingSetTopicAsId,
49
	WeightInfoBounds, WithComputedOrigin, WithUniqueTopic,
50
};
51

            
52
use parachains_common::message_queue::{NarrowOriginToSibling, ParaIdToSibling};
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, JustTry};
62

            
63
use cumulus_primitives_core::{AggregateMessageOrigin, ParaId};
64
use pallet_xcm::EnsureXcm;
65
use xcm_primitives::{
66
	AbsoluteAndRelativeReserve, AccountIdToCurrencyId, AccountIdToLocation, AsAssetType,
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, 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::Kusama;
86
	// The relay chain Origin type
87
	pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into();
88
	// The universal location within the global consensus system
89
	pub UniversalLocation: InteriorLocation =
90
		[GlobalConsensus(RelayNetwork::get()), Parachain(ParachainInfo::parachain_id().into())].into();
91

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

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

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

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

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

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

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

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

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

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

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

            
239
pub struct SafeCallFilter;
240
impl frame_support::traits::Contains<RuntimeCall> for SafeCallFilter {
241
	fn contains(_call: &RuntimeCall) -> bool {
242
		// TODO review
243
		// This needs to be addressed at EVM level
244
		true
245
	}
246
}
247

            
248
parameter_types! {
249
	/// Location of Asset Hub
250
	pub AssetHubLocation: Location = Location::new(1, [Parachain(1000)]);
251
	pub const RelayLocation: Location = Location::parent();
252
	pub RelayLocationFilter: AssetFilter = Wild(AllOf {
253
		fun: WildFungible,
254
		id: xcm::prelude::AssetId(RelayLocation::get()),
255
	});
256
	pub RelayChainNativeAssetFromAssetHub: (AssetFilter, Location) = (
257
		RelayLocationFilter::get(),
258
		AssetHubLocation::get()
259
	);
260
	pub const MaxAssetsIntoHolding: u32 = xcm_primitives::MAX_ASSETS;
261
}
262

            
263
type Reserves = (
264
	// Assets bridged from different consensus systems held in reserve on Asset Hub.
265
	IsBridgedConcreteAssetFrom<AssetHubLocation>,
266
	// Assets bridged from Moonbeam
267
	IsBridgedConcreteAssetFrom<bp_moonbeam::GlobalConsensusLocation>,
268
	// Relaychain (DOT) from Asset Hub
269
	Case<RelayChainNativeAssetFromAssetHub>,
270
	// Assets which the reserve is the same as the origin.
271
	MultiNativeAsset<AbsoluteAndRelativeReserve<SelfLocationAbsolute>>,
272
);
273

            
274
// Our implementation of the Moonbeam Call
275
// Attaches the right origin in case the call is made to pallet-ethereum-xcm
276
#[cfg(not(feature = "evm-tracing"))]
277
moonbeam_runtime_common::impl_moonbeam_xcm_call!();
278
#[cfg(feature = "evm-tracing")]
279
moonbeam_runtime_common::impl_moonbeam_xcm_call_tracing!();
280

            
281
moonbeam_runtime_common::impl_evm_runner_precompile_or_eth_xcm!();
282

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

            
324
pub type XcmExecutor = pallet_erc20_xcm_bridge::XcmExecutorWrapper<
325
	XcmExecutorConfig,
326
	xcm_executor::XcmExecutor<XcmExecutorConfig>,
327
>;
328

            
329
// Converts a Signed Local Origin into a Location
330
pub type LocalOriginToLocation = SignedToAccountId20<RuntimeOrigin, AccountId, RelayNetwork>;
331

            
332
/// For routing XCM messages which do not cross local consensus boundary.
333
pub type LocalXcmRouter = (
334
	// Two routers - use UMP to communicate with the relay chain:
335
	cumulus_primitives_utility::ParentAsUmp<ParachainSystem, PolkadotXcm, ()>,
336
	// ..and XCMP to communicate with the sibling chains.
337
	XcmpQueue,
338
);
339

            
340
/// The means for routing XCM messages which are not for local execution into the right message
341
/// queues.
342
pub type XcmRouter = WithUniqueTopic<(
343
	// The means for routing XCM messages which are not for local execution into the right message
344
	// queues.
345
	LocalXcmRouter,
346
	// Router that exports messages to be delivered to the Polkadot GlobalConsensus
347
	moonbeam_runtime_common::bridge::BridgeXcmRouter<
348
		xcm_builder::LocalExporter<BridgeXcmOverMoonbeam, UniversalLocation>,
349
	>,
350
)>;
351

            
352
impl pallet_xcm::Config for Runtime {
353
	type RuntimeEvent = RuntimeEvent;
354
	type SendXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
355
	type XcmRouter = XcmRouter;
356
	type ExecuteXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
357
	type XcmExecuteFilter = Nothing;
358
	type XcmExecutor = XcmExecutor;
359
	type XcmTeleportFilter = Nothing;
360
	type XcmReserveTransferFilter = Everything;
361
	type Weigher = XcmWeigher;
362
	type UniversalLocation = UniversalLocation;
363
	type RuntimeOrigin = RuntimeOrigin;
364
	type RuntimeCall = RuntimeCall;
365
	const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
366
	type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
367
	type Currency = Balances;
368
	type CurrencyMatcher = ();
369
	type TrustedLockers = ();
370
	type SovereignAccountOf = LocationToAccountId;
371
	type MaxLockers = ConstU32<8>;
372
	type MaxRemoteLockConsumers = ConstU32<0>;
373
	type RemoteLockConsumerIdentifier = ();
374
	type WeightInfo = moonriver_weights::pallet_xcm::WeightInfo<Runtime>;
375
	type AdminOrigin = EnsureRoot<AccountId>;
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 = moonriver_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 = moonriver_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
14
#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
469
pub enum AssetType {
470
27
	Xcm(xcm::v3::Location),
471
}
472
impl Default for AssetType {
473
	fn default() -> Self {
474
		Self::Xcm(xcm::v3::Location::here())
475
	}
476
}
477

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

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

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

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

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

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

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

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

            
565
// How to convert from CurrencyId to Location
566
pub struct CurrencyIdToLocation<AssetXConverter>(sp_std::marker::PhantomData<AssetXConverter>);
567
impl<AssetXConverter> sp_runtime::traits::Convert<CurrencyId, Option<Location>>
568
	for CurrencyIdToLocation<AssetXConverter>
569
where
570
	AssetXConverter: MaybeEquivalence<Location, AssetId>,
571
{
572
7
	fn convert(currency: CurrencyId) -> Option<Location> {
573
7
		match currency {
574
			// For now and until Xtokens is adapted to handle 0.9.16 version we use
575
			// the old anchoring here
576
			// This is not a problem in either cases, since the view of the destination
577
			// chain does not change
578
			// TODO! change this to NewAnchoringSelfReserve once xtokens is adapted for it
579
			CurrencyId::SelfReserve => {
580
1
				let multi: Location = SelfReserve::get();
581
1
				Some(multi)
582
			}
583
6
			CurrencyId::ForeignAsset(asset) => AssetXConverter::convert_back(&asset),
584
			CurrencyId::Erc20 { contract_address } => {
585
				let mut location = Erc20XcmBridgePalletLocation::get();
586
				location
587
					.push_interior(Junction::AccountKey20 {
588
						key: contract_address.0,
589
						network: None,
590
					})
591
					.ok();
592
				Some(location)
593
			}
594
		}
595
7
	}
596
}
597

            
598
parameter_types! {
599
	pub const BaseXcmWeight: Weight = Weight::from_parts(200_000_000u64, 0);
600
	pub const MaxAssetsForTransfer: usize = 2;
601

            
602
	// This is how we are going to detect whether the asset is a Reserve asset
603
	// This however is the chain part only
604
	pub SelfLocation: Location = Location::here();
605
	// We need this to be able to catch when someone is trying to execute a non-
606
	// cross-chain transfer in xtokens through the absolute path way
607
	pub SelfLocationAbsolute: Location = Location {
608
		parents:1,
609
		interior: [
610
			Parachain(ParachainInfo::parachain_id().into())
611
		].into()
612
	};
613
}
614

            
615
// 1 KSM 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
7
#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
624
pub enum Transactors {
625
	Relay,
626
}
627

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

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

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

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

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

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

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

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

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

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

            
723
pub struct EvmForeignAssetIdFilter;
724
impl frame_support::traits::Contains<AssetId> for EvmForeignAssetIdFilter {
725
21
	fn contains(asset_id: &AssetId) -> bool {
726
		use xcm_primitives::AssetTypeGetter as _;
727
		// We should return true only if the AssetId doesn't exist in AssetManager
728
21
		AssetManager::get_asset_type(*asset_id).is_none()
729
21
	}
730
}
731

            
732
pub type ForeignAssetManagerOrigin = EitherOf<
733
	MapSuccessToXcm<EnsureXcm<AllowSiblingParachains>>,
734
	MapSuccessToGovernance<
735
		EitherOf<
736
			EnsureRoot<AccountId>,
737
			EitherOf<
738
				pallet_collective::EnsureProportionMoreThan<
739
					AccountId,
740
					OpenTechCommitteeInstance,
741
					5,
742
					9,
743
				>,
744
				EitherOf<
745
					governance::custom_origins::FastGeneralAdmin,
746
					governance::custom_origins::GeneralAdmin,
747
				>,
748
			>,
749
		>,
750
	>,
751
>;
752
impl pallet_moonbeam_foreign_assets::Config for Runtime {
753
	type AccountIdToH160 = AccountIdToH160;
754
	type AssetIdFilter = EvmForeignAssetIdFilter;
755
	type EvmRunner = EvmRunnerPrecompileOrEthXcm<MoonbeamCall, Self>;
756
	type ConvertLocation =
757
		SiblingParachainConvertsVia<polkadot_parachain::primitives::Sibling, AccountId>;
758
	type ForeignAssetCreatorOrigin = ForeignAssetManagerOrigin;
759
	type ForeignAssetFreezerOrigin = ForeignAssetManagerOrigin;
760
	type ForeignAssetModifierOrigin = ForeignAssetManagerOrigin;
761
	type ForeignAssetUnfreezerOrigin = ForeignAssetManagerOrigin;
762
	type OnForeignAssetCreated = ();
763
	type MaxForeignAssets = ConstU32<256>;
764
	type RuntimeEvent = RuntimeEvent;
765
	type WeightInfo = moonriver_weights::pallet_moonbeam_foreign_assets::WeightInfo<Runtime>;
766
	type XcmLocationToH160 = LocationToH160;
767
	type ForeignAssetCreationDeposit = dynamic_params::xcm_config::ForeignAssetCreationDeposit;
768
	type Currency = Balances;
769
	type Balance = Balance;
770
}
771

            
772
pub struct AssetFeesFilter;
773
impl frame_support::traits::Contains<Location> for AssetFeesFilter {
774
21
	fn contains(location: &Location) -> bool {
775
21
		location.parent_count() > 0
776
21
			&& location.first_interior() != Erc20XcmBridgePalletLocation::get().first_interior()
777
21
	}
778
}
779

            
780
pub type AddAndEditSupportedAssetOrigin = EitherOfDiverse<
781
	EnsureRoot<AccountId>,
782
	EitherOfDiverse<
783
		pallet_collective::EnsureProportionMoreThan<AccountId, OpenTechCommitteeInstance, 5, 9>,
784
		EitherOf<
785
			governance::custom_origins::GeneralAdmin,
786
			governance::custom_origins::FastGeneralAdmin,
787
		>,
788
	>,
789
>;
790

            
791
pub type RemoveSupportedAssetOrigin = EitherOfDiverse<
792
	EnsureRoot<AccountId>,
793
	pallet_collective::EnsureProportionMoreThan<AccountId, OpenTechCommitteeInstance, 5, 9>,
794
>;
795

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

            
815
#[cfg(feature = "runtime-benchmarks")]
816
mod testing {
817
	use super::*;
818

            
819
	/// This From exists for benchmarking purposes. It has the potential side-effect of calling
820
	/// AssetManager::set_asset_type_asset_id() and should NOT be used in any production code.
821
	impl From<Location> for CurrencyId {
822
		fn from(location: Location) -> CurrencyId {
823
			use xcm_primitives::AssetTypeGetter;
824

            
825
			// If it does not exist, for benchmarking purposes, we create the association
826
			let asset_id = if let Some(asset_id) =
827
				AsAssetType::<AssetId, AssetType, AssetManager>::convert_location(&location)
828
			{
829
				asset_id
830
			} else {
831
				let asset_type: AssetType = location
832
					.try_into()
833
					.expect("Location convertion to AssetType should succeed");
834
				let asset_id: AssetId = asset_type.clone().into();
835
				AssetManager::set_asset_type_asset_id(asset_type, asset_id);
836
				asset_id
837
			};
838

            
839
			CurrencyId::ForeignAsset(asset_id)
840
		}
841
	}
842
}