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, BridgeXcmOverMoonbeam,
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::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, 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::Kusama;
85
	// The relay chain Origin type
86
	pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into();
87
	// The universal location within the global consensus system
88
	pub UniversalLocation: InteriorLocation =
89
		[GlobalConsensus(RelayNetwork::get()), Parachain(ParachainInfo::parachain_id().into())].into();
90

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

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

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

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

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

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

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

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

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

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

            
209
pub struct SafeCallFilter;
210
impl frame_support::traits::Contains<RuntimeCall> for SafeCallFilter {
211
	fn contains(_call: &RuntimeCall) -> bool {
212
		// TODO review
213
		// This needs to be addressed at EVM level
214
		true
215
	}
216
}
217

            
218
parameter_types! {
219
	/// Location of Asset Hub
220
	pub AssetHubLocation: Location = Location::new(1, [Parachain(1000)]);
221
	pub const RelayLocation: Location = Location::parent();
222
	pub RelayLocationFilter: AssetFilter = Wild(AllOf {
223
		fun: WildFungible,
224
		id: xcm::prelude::AssetId(RelayLocation::get()),
225
	});
226
	pub RelayChainNativeAssetFromAssetHub: (AssetFilter, Location) = (
227
		RelayLocationFilter::get(),
228
		AssetHubLocation::get()
229
	);
230
	pub const MaxAssetsIntoHolding: u32 = xcm_primitives::MAX_ASSETS;
231
}
232

            
233
type Reserves = (
234
	// Assets bridged from different consensus systems held in reserve on Asset Hub.
235
	IsBridgedConcreteAssetFrom<AssetHubLocation>,
236
	// Assets bridged from Moonbeam
237
	IsBridgedConcreteAssetFrom<bp_moonbeam::GlobalConsensusLocation>,
238
	// Relaychain (DOT) from Asset Hub
239
	Case<RelayChainNativeAssetFromAssetHub>,
240
	// Assets which the reserve is the same as the origin.
241
	MultiNativeAsset<AbsoluteAndRelativeReserve<SelfLocationAbsolute>>,
242
);
243

            
244
// Our implementation of the Moonbeam Call
245
// Attaches the right origin in case the call is made to pallet-ethereum-xcm
246
#[cfg(not(feature = "evm-tracing"))]
247
moonbeam_runtime_common::impl_moonbeam_xcm_call!();
248
#[cfg(feature = "evm-tracing")]
249
moonbeam_runtime_common::impl_moonbeam_xcm_call_tracing!();
250

            
251
moonbeam_runtime_common::impl_evm_runner_precompile_or_eth_xcm!();
252

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

            
295
pub type XcmExecutor = pallet_erc20_xcm_bridge::XcmExecutorWrapper<
296
	XcmExecutorConfig,
297
	xcm_executor::XcmExecutor<XcmExecutorConfig>,
298
>;
299

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

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

            
311
/// The means for routing XCM messages which are not for local execution into the right message
312
/// queues.
313
pub type XcmRouter = WithUniqueTopic<(
314
	// The means for routing XCM messages which are not for local execution into the right message
315
	// queues.
316
	LocalXcmRouter,
317
	// Router that exports messages to be delivered to the Polkadot GlobalConsensus
318
	moonbeam_runtime_common::bridge::BridgeXcmRouter<
319
		xcm_builder::LocalExporter<BridgeXcmOverMoonbeam, UniversalLocation>,
320
	>,
321
)>;
322

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

            
350
impl cumulus_pallet_xcm::Config for Runtime {
351
	type RuntimeEvent = RuntimeEvent;
352
	type XcmExecutor = XcmExecutor;
353
}
354

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

            
373
parameter_types! {
374
	pub const RelayOrigin: AggregateMessageOrigin = AggregateMessageOrigin::Parent;
375
}
376

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

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

            
419
pub type FastAuthorizeUpgradeOrigin = EitherOfDiverse<
420
	EnsureRoot<AccountId>,
421
	pallet_collective::EnsureProportionAtLeast<AccountId, OpenTechCommitteeInstance, 5, 9>,
422
>;
423

            
424
pub type ResumeXcmOrigin = EitherOfDiverse<
425
	EnsureRoot<AccountId>,
426
	pallet_collective::EnsureProportionAtLeast<AccountId, OpenTechCommitteeInstance, 5, 9>,
427
>;
428

            
429
impl pallet_emergency_para_xcm::Config for Runtime {
430
	type CheckAssociatedRelayNumber =
431
		cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases;
432
	type QueuePausedQuery = (MaintenanceMode, NarrowOriginToSibling<XcmpQueue>);
433
	type PausedThreshold = ConstU32<300>;
434
	type FastAuthorizeUpgradeOrigin = FastAuthorizeUpgradeOrigin;
435
	type PausedToNormalOrigin = ResumeXcmOrigin;
436
}
437

            
438
// Our AssetType. For now we only handle Xcm Assets
439
#[derive(
440
	Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo, DecodeWithMemTracking,
441
)]
442
pub enum AssetType {
443
	Xcm(xcm::v3::Location),
444
}
445
impl Default for AssetType {
446
	fn default() -> Self {
447
		Self::Xcm(xcm::v3::Location::here())
448
	}
449
}
450

            
451
impl From<xcm::v3::Location> for AssetType {
452
	fn from(location: xcm::v3::Location) -> Self {
453
		Self::Xcm(location)
454
	}
455
}
456

            
457
// This can be removed once we fully adopt xcm::v5 everywhere
458
impl TryFrom<Location> for AssetType {
459
	type Error = ();
460

            
461
77
	fn try_from(location: Location) -> Result<Self, Self::Error> {
462
		// Convert the V5 location to a V3 location
463
77
		match xcm::VersionedLocation::V5(location).into_version(xcm::v3::VERSION) {
464
77
			Ok(xcm::VersionedLocation::V3(loc)) => Ok(AssetType::Xcm(loc.into())),
465
			// Any other version or conversion error returns an error
466
			_ => Err(()),
467
		}
468
77
	}
469
}
470

            
471
impl Into<Option<xcm::v3::Location>> for AssetType {
472
	fn into(self) -> Option<xcm::v3::Location> {
473
		match self {
474
			Self::Xcm(location) => Some(location),
475
		}
476
	}
477
}
478

            
479
impl Into<Option<Location>> for AssetType {
480
	fn into(self) -> Option<Location> {
481
		match self {
482
			Self::Xcm(location) => {
483
				let versioned = xcm::VersionedLocation::V3(location);
484
				match versioned.into_version(xcm::latest::VERSION) {
485
					Ok(xcm::VersionedLocation::V5(loc)) => Some(loc),
486
					_ => None,
487
				}
488
			}
489
		}
490
	}
491
}
492

            
493
// Implementation on how to retrieve the AssetId from an AssetType
494
// We simply hash the AssetType and take the lowest 128 bits
495
impl From<AssetType> for AssetId {
496
294
	fn from(asset: AssetType) -> AssetId {
497
294
		match asset {
498
294
			AssetType::Xcm(id) => {
499
294
				let mut result: [u8; 16] = [0u8; 16];
500
294
				let hash: H256 = id.using_encoded(<Runtime as frame_system::Config>::Hashing::hash);
501
294
				result.copy_from_slice(&hash.as_fixed_bytes()[0..16]);
502
294
				u128::from_le_bytes(result)
503
			}
504
		}
505
294
	}
506
}
507

            
508
// Our currencyId. We distinguish for now between SelfReserve, and Others, defined by their Id.
509
#[derive(
510
	Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo, DecodeWithMemTracking,
511
)]
512
pub enum CurrencyId {
513
	// Our native token
514
	SelfReserve,
515
	// Assets representing other chains native tokens
516
	ForeignAsset(AssetId),
517
	// Erc20 token
518
	Erc20 { contract_address: H160 },
519
}
520

            
521
impl AccountIdToCurrencyId<AccountId, CurrencyId> for Runtime {
522
	fn account_to_currency_id(account: AccountId) -> Option<CurrencyId> {
523
		Some(match account {
524
			// the self-reserve currency is identified by the pallet-balances address
525
			a if a == H160::from_low_u64_be(2050).into() => CurrencyId::SelfReserve,
526
			// the rest of the currencies, by their corresponding erc20 address
527
			_ => match Runtime::account_to_asset_id(account) {
528
				// A foreign asset
529
				Some((_prefix, asset_id)) => CurrencyId::ForeignAsset(asset_id),
530
				// If no known prefix is identified, we consider that it's a "real" erc20 token
531
				// (i.e. managed by a real smart contract)
532
				None => CurrencyId::Erc20 {
533
					contract_address: account.into(),
534
				},
535
			},
536
		})
537
	}
538
}
539

            
540
// How to convert from CurrencyId to Location
541
pub struct CurrencyIdToLocation<AssetXConverter>(sp_std::marker::PhantomData<AssetXConverter>);
542
impl<AssetXConverter> sp_runtime::traits::Convert<CurrencyId, Option<Location>>
543
	for CurrencyIdToLocation<AssetXConverter>
544
where
545
	AssetXConverter: MaybeEquivalence<Location, AssetId>,
546
{
547
5
	fn convert(currency: CurrencyId) -> Option<Location> {
548
5
		match currency {
549
			// For now and until Xtokens is adapted to handle 0.9.16 version we use
550
			// the old anchoring here
551
			// This is not a problem in either cases, since the view of the destination
552
			// chain does not change
553
			// TODO! change this to NewAnchoringSelfReserve once xtokens is adapted for it
554
			CurrencyId::SelfReserve => {
555
1
				let multi: Location = SelfReserve::get();
556
1
				Some(multi)
557
			}
558
4
			CurrencyId::ForeignAsset(asset) => AssetXConverter::convert_back(&asset),
559
			CurrencyId::Erc20 { contract_address } => {
560
				let mut location = Erc20XcmBridgePalletLocation::get();
561
				location
562
					.push_interior(Junction::AccountKey20 {
563
						key: contract_address.0,
564
						network: None,
565
					})
566
					.ok();
567
				Some(location)
568
			}
569
		}
570
5
	}
571
}
572

            
573
parameter_types! {
574
	pub const BaseXcmWeight: Weight = Weight::from_parts(200_000_000u64, 0);
575
	pub const MaxAssetsForTransfer: usize = 2;
576

            
577
	// This is how we are going to detect whether the asset is a Reserve asset
578
	// This however is the chain part only
579
	pub SelfLocation: Location = Location::here();
580
	// We need this to be able to catch when someone is trying to execute a non-
581
	// cross-chain transfer in xtokens through the absolute path way
582
	pub SelfLocationAbsolute: Location = Location {
583
		parents:1,
584
		interior: [
585
			Parachain(ParachainInfo::parachain_id().into())
586
		].into()
587
	};
588
}
589

            
590
// 1 KSM should be enough
591
parameter_types! {
592
	pub MaxHrmpRelayFee: Asset = (Location::parent(), 1_000_000_000_000u128).into();
593
}
594

            
595
// For now we only allow to transact in the relay, although this might change in the future
596
// Transactors just defines the chains in which we allow transactions to be issued through
597
// xcm
598
#[derive(
599
	Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo, DecodeWithMemTracking,
600
)]
601
pub enum Transactors {
602
	Relay,
603
	AssetHub,
604
}
605

            
606
// Default for benchmarking
607
#[cfg(feature = "runtime-benchmarks")]
608
impl Default for Transactors {
609
	fn default() -> Self {
610
		Transactors::Relay
611
	}
612
}
613

            
614
impl TryFrom<u8> for Transactors {
615
	type Error = ();
616
	fn try_from(value: u8) -> Result<Self, Self::Error> {
617
		match value {
618
			0u8 => Ok(Transactors::Relay),
619
			1u8 => Ok(Transactors::AssetHub),
620
			_ => Err(()),
621
		}
622
	}
623
}
624

            
625
impl XcmTransact for Transactors {
626
35
	fn destination(self) -> Location {
627
35
		match self {
628
35
			Transactors::Relay => RelayLocation::get(),
629
			Transactors::AssetHub => AssetHubLocation::get(),
630
		}
631
35
	}
632

            
633
63
	fn utility_pallet_index(&self) -> u8 {
634
63
		match self {
635
63
			Transactors::Relay => pallet_xcm_transactor::RelayIndices::<Runtime>::get().utility,
636
			Transactors::AssetHub => pallet_xcm_transactor::ASSET_HUB_UTILITY_PALLET_INDEX,
637
		}
638
63
	}
639

            
640
	fn staking_pallet_index(&self) -> u8 {
641
		match self {
642
			Transactors::Relay => pallet_xcm_transactor::RelayIndices::<Runtime>::get().staking,
643
			Transactors::AssetHub => pallet_xcm_transactor::ASSET_HUB_STAKING_PALLET_INDEX,
644
		}
645
	}
646
}
647

            
648
pub type DerivativeAddressRegistrationOrigin =
649
	EitherOfDiverse<EnsureRoot<AccountId>, governance::custom_origins::GeneralAdmin>;
650

            
651
impl pallet_xcm_transactor::Config for Runtime {
652
	type Balance = Balance;
653
	type Transactor = Transactors;
654
	type DerivativeAddressRegistrationOrigin = DerivativeAddressRegistrationOrigin;
655
	type SovereignAccountDispatcherOrigin = EnsureRoot<AccountId>;
656
	type CurrencyId = CurrencyId;
657
	type AccountIdToLocation = AccountIdToLocation<AccountId>;
658
	type CurrencyIdToLocation = CurrencyIdToLocation<(EvmForeignAssets,)>;
659
	type XcmSender = XcmRouter;
660
	type SelfLocation = SelfLocation;
661
	type Weigher = XcmWeigher;
662
	type UniversalLocation = UniversalLocation;
663
	type BaseXcmWeight = BaseXcmWeight;
664
	type AssetTransactor = AssetTransactors;
665
	type ReserveProvider = AbsoluteAndRelativeReserve<SelfLocationAbsolute>;
666
	type WeightInfo = moonriver_weights::pallet_xcm_transactor::WeightInfo<Runtime>;
667
	type HrmpManipulatorOrigin = GeneralAdminOrRoot;
668
	type HrmpOpenOrigin = FastGeneralAdminOrRoot;
669
	type MaxHrmpFee = xcm_builder::Case<MaxHrmpRelayFee>;
670
}
671

            
672
parameter_types! {
673
	// This is the relative view of erc20 assets.
674
	// Identified by this prefix + AccountKey20(contractAddress)
675
	// We use the RELATIVE multilocation
676
	pub Erc20XcmBridgePalletLocation: Location = Location {
677
		parents:0,
678
		interior: [
679
			PalletInstance(<Erc20XcmBridge as PalletInfoAccess>::index() as u8)
680
		].into()
681
	};
682

            
683
	// To be able to support almost all erc20 implementations,
684
	// we provide a sufficiently hight gas limit.
685
	pub Erc20XcmBridgeTransferGasLimit: u64 = 400_000;
686
}
687

            
688
impl pallet_erc20_xcm_bridge::Config for Runtime {
689
	type AccountIdConverter = LocationToH160;
690
	type Erc20MultilocationPrefix = Erc20XcmBridgePalletLocation;
691
	type Erc20TransferGasLimit = Erc20XcmBridgeTransferGasLimit;
692
	type EvmRunner = EvmRunnerPrecompileOrEthXcm<MoonbeamCall, Self>;
693
}
694

            
695
pub struct AccountIdToH160;
696
impl sp_runtime::traits::Convert<AccountId, H160> for AccountIdToH160 {
697
329
	fn convert(account_id: AccountId) -> H160 {
698
329
		account_id.into()
699
329
	}
700
}
701

            
702
pub type ForeignAssetManagerOrigin = EitherOf<
703
	MapSuccessToXcm<EnsureXcm<AllowSiblingParachains>>,
704
	MapSuccessToGovernance<
705
		EitherOf<
706
			EnsureRoot<AccountId>,
707
			EitherOf<
708
				pallet_collective::EnsureProportionMoreThan<
709
					AccountId,
710
					OpenTechCommitteeInstance,
711
					5,
712
					9,
713
				>,
714
				EitherOf<
715
					governance::custom_origins::FastGeneralAdmin,
716
					governance::custom_origins::GeneralAdmin,
717
				>,
718
			>,
719
		>,
720
	>,
721
>;
722
impl pallet_moonbeam_foreign_assets::Config for Runtime {
723
	type AccountIdToH160 = AccountIdToH160;
724
	type AssetIdFilter = Everything;
725
	type EvmRunner = EvmRunnerPrecompileOrEthXcm<MoonbeamCall, Self>;
726
	type ConvertLocation =
727
		SiblingParachainConvertsVia<polkadot_parachain::primitives::Sibling, AccountId>;
728
	type ForeignAssetCreatorOrigin = ForeignAssetManagerOrigin;
729
	type ForeignAssetFreezerOrigin = ForeignAssetManagerOrigin;
730
	type ForeignAssetModifierOrigin = ForeignAssetManagerOrigin;
731
	type ForeignAssetUnfreezerOrigin = ForeignAssetManagerOrigin;
732
	type OnForeignAssetCreated = ();
733
	type MaxForeignAssets = ConstU32<256>;
734
	type WeightInfo = moonriver_weights::pallet_moonbeam_foreign_assets::WeightInfo<Runtime>;
735
	type XcmLocationToH160 = LocationToH160;
736
	type ForeignAssetCreationDeposit = dynamic_params::xcm_config::ForeignAssetCreationDeposit;
737
	type Currency = Balances;
738
	type Balance = Balance;
739
}
740

            
741
pub struct AssetFeesFilter;
742
impl frame_support::traits::Contains<Location> for AssetFeesFilter {
743
35
	fn contains(location: &Location) -> bool {
744
35
		location.parent_count() > 0
745
35
			&& location.first_interior() != Erc20XcmBridgePalletLocation::get().first_interior()
746
35
	}
747
}
748

            
749
pub type AddAndEditSupportedAssetOrigin = EitherOfDiverse<
750
	EnsureRoot<AccountId>,
751
	EitherOfDiverse<
752
		pallet_collective::EnsureProportionMoreThan<AccountId, OpenTechCommitteeInstance, 5, 9>,
753
		EitherOf<
754
			governance::custom_origins::GeneralAdmin,
755
			governance::custom_origins::FastGeneralAdmin,
756
		>,
757
	>,
758
>;
759

            
760
pub type RemoveSupportedAssetOrigin = EitherOfDiverse<
761
	EnsureRoot<AccountId>,
762
	pallet_collective::EnsureProportionMoreThan<AccountId, OpenTechCommitteeInstance, 5, 9>,
763
>;
764

            
765
impl pallet_xcm_weight_trader::Config for Runtime {
766
	type AccountIdToLocation = AccountIdToLocation<AccountId>;
767
	type AddSupportedAssetOrigin = AddAndEditSupportedAssetOrigin;
768
	type AssetLocationFilter = AssetFeesFilter;
769
	type AssetTransactor = AssetTransactors;
770
	type Balance = Balance;
771
	type EditSupportedAssetOrigin = AddAndEditSupportedAssetOrigin;
772
	type NativeLocation = SelfReserve;
773
	type PauseSupportedAssetOrigin = AddAndEditSupportedAssetOrigin;
774
	type ResumeSupportedAssetOrigin = AddAndEditSupportedAssetOrigin;
775
	type RemoveSupportedAssetOrigin = RemoveSupportedAssetOrigin;
776
	type WeightInfo = moonriver_weights::pallet_xcm_weight_trader::WeightInfo<Runtime>;
777
	type WeightToFee = <Runtime as pallet_transaction_payment::Config>::WeightToFee;
778
	type XcmFeesAccount = XcmFeesAccount;
779
	#[cfg(feature = "runtime-benchmarks")]
780
	type NotFilteredLocation = RelayLocation;
781
}
782

            
783
#[cfg(feature = "runtime-benchmarks")]
784
mod testing {
785
	use super::*;
786

            
787
	/// This From exists for benchmarking purposes. It has the potential side-effect of calling
788
	/// EvmForeignAssets::set_asset() and should NOT be used in any production code.
789
	impl From<Location> for CurrencyId {
790
		fn from(location: Location) -> CurrencyId {
791
			use sp_runtime::traits::MaybeEquivalence;
792

            
793
			// If it does not exist, for benchmarking purposes, we create the association
794
			let asset_id = if let Some(asset_id) = EvmForeignAssets::convert(&location) {
795
				asset_id
796
			} else {
797
				// Generate asset ID from location hash (similar to old AssetManager approach)
798
				let hash: H256 =
799
					location.using_encoded(<Runtime as frame_system::Config>::Hashing::hash);
800
				let mut result: [u8; 16] = [0u8; 16];
801
				result.copy_from_slice(&hash.as_fixed_bytes()[0..16]);
802
				let asset_id = u128::from_le_bytes(result);
803

            
804
				EvmForeignAssets::set_asset(location, asset_id);
805
				asset_id
806
			};
807

            
808
			CurrencyId::ForeignAsset(asset_id)
809
		}
810
	}
811
}