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, XcmWeightTrader,
25
	XcmpQueue,
26
};
27

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

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

            
43
use xcm_builder::{
44
	AccountKey20Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom,
45
	AllowTopLevelPaidExecutionFrom, Case, DescribeAllTerminal, DescribeFamily, EnsureXcmOrigin,
46
	ExternalConsensusLocationsConverterFor, FungibleAdapter as XcmCurrencyAdapter,
47
	HashedDescription, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative,
48
	SiblingParachainConvertsVia, SignedAccountKey20AsNative, SovereignSignedViaLocation,
49
	TakeWeightCredit, TrailingSetTopicAsId, 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};
62

            
63
use cumulus_primitives_core::{AggregateMessageOrigin, ParaId};
64
use frame_support::traits::Disabled;
65
use pallet_xcm::EnsureXcm;
66
use xcm_primitives::{
67
	AbsoluteAndRelativeReserve, AccountIdToCurrencyId, AccountIdToLocation,
68
	IsBridgedConcreteAssetFrom, MultiNativeAsset, SignedToAccountId20, XcmTransact,
69
};
70

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

            
83
parameter_types! {
84
	// The network Id of the relay
85
	pub const RelayNetwork: NetworkId = NetworkId::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
	ExternalConsensusLocationsConverterFor<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
568
	fn convert_location(location: &Location) -> Option<H160> {
125
568
		<LocationToAccountId as ConvertLocation<AccountId>>::convert_location(location)
126
568
			.map(Into::into)
127
568
	}
128
}
129

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

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

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

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

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

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

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

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

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

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

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

            
252
moonbeam_runtime_common::impl_evm_runner_precompile_or_eth_xcm!();
253

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
806
				EvmForeignAssets::set_asset(location, asset_id);
807
				asset_id
808
			};
809

            
810
			CurrencyId::ForeignAsset(asset_id)
811
		}
812
	}
813
}