1
// Copyright 2019-2022 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::moonbase_weights;
21
use super::{
22
	governance, AccountId, AssetId, AssetManager, Balance, Balances, EmergencyParaXcm,
23
	Erc20XcmBridge, EvmForeignAssets, MaintenanceMode, MessageQueue, ParachainInfo,
24
	ParachainSystem, Perbill, PolkadotXcm, Runtime, RuntimeBlockWeights, RuntimeCall, RuntimeEvent,
25
	RuntimeOrigin, Treasury, XcmpQueue,
26
};
27
use crate::OpenTechCommitteeInstance;
28
use moonkit_xcm_primitives::AccountIdAssetIdConversion;
29
use sp_runtime::{
30
	traits::{Hash as THash, MaybeEquivalence, PostDispatchInfoOf},
31
	DispatchErrorWithPostInfo,
32
};
33

            
34
use frame_support::{
35
	parameter_types,
36
	traits::{EitherOf, EitherOfDiverse, Everything, Nothing, PalletInfoAccess, TransformOrigin},
37
};
38

            
39
use frame_system::{EnsureRoot, RawOrigin};
40
use sp_core::{ConstU32, H160, H256};
41
use sp_weights::Weight;
42
use xcm_builder::{
43
	AccountKey20Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom,
44
	AllowTopLevelPaidExecutionFrom, Case, ConvertedConcreteId, DescribeAllTerminal, DescribeFamily,
45
	EnsureXcmOrigin, FungibleAdapter as XcmCurrencyAdapter, FungiblesAdapter, HashedDescription,
46
	NoChecking, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative,
47
	SiblingParachainConvertsVia, SignedAccountKey20AsNative, SovereignSignedViaLocation,
48
	TakeWeightCredit, TrailingSetTopicAsId, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic,
49
};
50

            
51
use parachains_common::message_queue::{NarrowOriginToSibling, ParaIdToSibling};
52

            
53
use xcm::latest::prelude::{
54
	AllOf, Asset, AssetFilter, GlobalConsensus, InteriorLocation, Junction, Location, NetworkId,
55
	PalletInstance, Parachain, Wild, WildFungible,
56
};
57

            
58
use xcm_executor::traits::{CallDispatcher, ConvertLocation, JustTry};
59

            
60
use cumulus_primitives_core::{AggregateMessageOrigin, ParaId};
61
use xcm_primitives::{
62
	AbsoluteAndRelativeReserve, AccountIdToCurrencyId, AccountIdToLocation, AsAssetType,
63
	IsBridgedConcreteAssetFrom, MultiNativeAsset, SignedToAccountId20, UtilityAvailableCalls,
64
	UtilityEncodeCall, XcmTransact,
65
};
66

            
67
use parity_scale_codec::{Decode, Encode};
68
use scale_info::TypeInfo;
69

            
70
use sp_core::Get;
71
use sp_std::{
72
	convert::{From, Into, TryFrom},
73
	prelude::*,
74
};
75

            
76
use crate::governance::referenda::{FastGeneralAdminOrRoot, GeneralAdminOrRoot};
77

            
78
parameter_types! {
79
	// The network Id of the relay
80
	pub const RelayNetwork: NetworkId = NetworkId::Westend;
81
	// The relay chain Origin type
82
	pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into();
83
	// The universal location within the global consensus system
84
	pub UniversalLocation: InteriorLocation =
85
		[GlobalConsensus(RelayNetwork::get()), Parachain(ParachainInfo::parachain_id().into())].into();
86

            
87

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

            
100
/// Type for specifying how a `Location` can be converted into an `AccountId`. This is used
101
/// when determining ownership of accounts for asset transacting and when attempting to use XCM
102
/// `Transact` in order to determine the dispatch Origin.
103
pub type LocationToAccountId = (
104
	// The parent (Relay-chain) origin converts to the default `AccountId`.
105
	ParentIsPreset<AccountId>,
106
	// Sibling parachain origins convert to AccountId via the `ParaId::into`.
107
	SiblingParachainConvertsVia<polkadot_parachain::primitives::Sibling, AccountId>,
108
	// If we receive a Location of type AccountKey20, just generate a native account
109
	AccountKey20Aliases<RelayNetwork, AccountId>,
110
	// Generate remote accounts according to polkadot standards
111
	HashedDescription<AccountId, DescribeFamily<DescribeAllTerminal>>,
112
);
113

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

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

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

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

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

            
197
parameter_types! {
198
	/// Maximum number of instructions in a single XCM fragment. A sanity check against
199
	/// weight caculations getting too crazy.
200
	pub MaxInstructions: u32 = 100;
201
}
202

            
203
/// Xcm Weigher shared between multiple Xcm-related configs.
204
pub type XcmWeigher = WeightInfoBounds<
205
	moonbeam_xcm_benchmarks::weights::XcmWeight<Runtime, RuntimeCall>,
206
	RuntimeCall,
207
	MaxInstructions,
208
>;
209

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

            
227
parameter_types! {
228
	/// Xcm fees will go to the treasury account
229
	pub XcmFeesAccount: AccountId = Treasury::account_id();
230
}
231

            
232
// Our implementation of the Moonbeam Call
233
// Attachs the right origin in case the call is made to pallet-ethereum-xcm
234
#[cfg(not(feature = "evm-tracing"))]
235
moonbeam_runtime_common::impl_moonbeam_xcm_call!();
236
#[cfg(feature = "evm-tracing")]
237
64
moonbeam_runtime_common::impl_moonbeam_xcm_call_tracing!();
238

            
239
16
moonbeam_runtime_common::impl_evm_runner_precompile_or_eth_xcm!();
240

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

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

            
265
type Reserves = (
266
	// Assets bridged from different consensus systems held in reserve on Asset Hub.
267
	IsBridgedConcreteAssetFrom<AssetHubLocation>,
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
pub struct XcmExecutorConfig;
275
impl xcm_executor::Config for XcmExecutorConfig {
276
	type RuntimeCall = RuntimeCall;
277
	type XcmSender = XcmRouter;
278
	// How to withdraw and deposit an asset.
279
	type AssetTransactor = AssetTransactors;
280
	type OriginConverter = XcmOriginToTransactDispatchOrigin;
281
	// Filter to the reserve withdraw operations
282
	// Whenever the reserve matches the relative or absolute value
283
	// of our chain, we always return the relative reserve
284
	type IsReserve = Reserves;
285
	type IsTeleporter = (); // No teleport
286
	type UniversalLocation = UniversalLocation;
287
	type Barrier = XcmBarrier;
288
	type Weigher = XcmWeigher;
289
	// As trader we use the XcmWeightTrader pallet.
290
	// For each foreign asset, the fee is computed based on its relative price (also
291
	// stored in the XcmWeightTrader pallet) against the native asset.
292
	// For the native asset fee is computed using WeightToFee implementation.
293
	type Trader = pallet_xcm_weight_trader::Trader<Runtime>;
294
	type ResponseHandler = PolkadotXcm;
295
	type SubscriptionService = PolkadotXcm;
296
	type AssetTrap = pallet_erc20_xcm_bridge::AssetTrapWrapper<PolkadotXcm, Runtime>;
297
	type AssetClaims = PolkadotXcm;
298
	type CallDispatcher = MoonbeamCall;
299
	type PalletInstancesInfo = crate::AllPalletsWithSystem;
300
	type MaxAssetsIntoHolding = MaxAssetsIntoHolding;
301
	type AssetLocker = ();
302
	type AssetExchanger = ();
303
	type FeeManager = ();
304
	type MessageExporter = ();
305
	type UniversalAliases = Nothing;
306
	type SafeCallFilter = SafeCallFilter;
307
	type Aliasers = Nothing;
308
	type TransactionalProcessor = xcm_builder::FrameTransactionalProcessor;
309
	type HrmpNewChannelOpenRequestHandler = ();
310
	type HrmpChannelAcceptedHandler = ();
311
	type HrmpChannelClosingHandler = ();
312
	type XcmRecorder = PolkadotXcm;
313
}
314

            
315
// Converts a Signed Local Origin into a Location
316
pub type LocalOriginToLocation = SignedToAccountId20<RuntimeOrigin, AccountId, RelayNetwork>;
317

            
318
/// The means for routing XCM messages which are not for local execution into the right message
319
/// queues.
320
pub type XcmRouter = WithUniqueTopic<(
321
	// Two routers - use UMP to communicate with the relay chain:
322
	cumulus_primitives_utility::ParentAsUmp<ParachainSystem, PolkadotXcm, ()>,
323
	// ..and XCMP to communicate with the sibling chains.
324
	XcmpQueue,
325
)>;
326

            
327
type XcmExecutor = pallet_erc20_xcm_bridge::XcmExecutorWrapper<
328
	XcmExecutorConfig,
329
	xcm_executor::XcmExecutor<XcmExecutorConfig>,
330
>;
331

            
332
impl pallet_xcm::Config for Runtime {
333
	type RuntimeEvent = RuntimeEvent;
334
	type SendXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
335
	type XcmRouter = XcmRouter;
336
	type ExecuteXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
337
	type XcmExecuteFilter = Everything;
338
	type XcmExecutor = XcmExecutor;
339
	type XcmTeleportFilter = Nothing;
340
	type XcmReserveTransferFilter = Everything;
341
	type Weigher = XcmWeigher;
342
	type UniversalLocation = UniversalLocation;
343
	type RuntimeOrigin = RuntimeOrigin;
344
	type RuntimeCall = RuntimeCall;
345
	const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
346
	type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion;
347
	type Currency = Balances;
348
	type CurrencyMatcher = ();
349
	type TrustedLockers = ();
350
	type SovereignAccountOf = LocationToAccountId;
351
	type MaxLockers = ConstU32<8>;
352
	type MaxRemoteLockConsumers = ConstU32<0>;
353
	type RemoteLockConsumerIdentifier = ();
354
	type WeightInfo = moonbase_weights::pallet_xcm::WeightInfo<Runtime>;
355
	type AdminOrigin = EnsureRoot<AccountId>;
356
}
357

            
358
impl cumulus_pallet_xcm::Config for Runtime {
359
	type RuntimeEvent = RuntimeEvent;
360
	type XcmExecutor = XcmExecutor;
361
}
362

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

            
381
parameter_types! {
382
	pub const RelayOrigin: AggregateMessageOrigin = AggregateMessageOrigin::Parent;
383
}
384

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

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

            
427
pub type FastAuthorizeUpgradeOrigin = EitherOfDiverse<
428
	EnsureRoot<AccountId>,
429
	pallet_collective::EnsureProportionAtLeast<AccountId, OpenTechCommitteeInstance, 5, 9>,
430
>;
431

            
432
pub type ResumeXcmOrigin = EitherOfDiverse<
433
	EnsureRoot<AccountId>,
434
	pallet_collective::EnsureProportionAtLeast<AccountId, OpenTechCommitteeInstance, 5, 9>,
435
>;
436

            
437
impl pallet_emergency_para_xcm::Config for Runtime {
438
	type RuntimeEvent = RuntimeEvent;
439
	type CheckAssociatedRelayNumber =
440
		cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases;
441
	type QueuePausedQuery = (MaintenanceMode, NarrowOriginToSibling<XcmpQueue>);
442
	type PausedThreshold = ConstU32<300>;
443
	type FastAuthorizeUpgradeOrigin = FastAuthorizeUpgradeOrigin;
444
	type PausedToNormalOrigin = ResumeXcmOrigin;
445
}
446

            
447
// Our AssetType. For now we only handle Xcm Assets
448
8
#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
449
pub enum AssetType {
450
22
	Xcm(xcm::v3::Location),
451
}
452
impl Default for AssetType {
453
	fn default() -> Self {
454
		Self::Xcm(xcm::v3::Location::here())
455
	}
456
}
457

            
458
impl From<xcm::v3::Location> for AssetType {
459
536
	fn from(location: xcm::v3::Location) -> Self {
460
536
		Self::Xcm(location)
461
536
	}
462
}
463

            
464
// This can be removed once we fully adopt xcm::v4 everywhere
465
impl TryFrom<Location> for AssetType {
466
	type Error = ();
467
	fn try_from(location: Location) -> Result<Self, Self::Error> {
468
		Ok(Self::Xcm(location.try_into()?))
469
	}
470
}
471

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

            
480
impl Into<Option<Location>> for AssetType {
481
	fn into(self) -> Option<Location> {
482
		match self {
483
			Self::Xcm(location) => {
484
				xcm_builder::WithLatestLocationConverter::convert_back(&location)
485
			}
486
		}
487
	}
488
}
489

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

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

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

            
535
// How to convert from CurrencyId to Location
536
pub struct CurrencyIdToLocation<AssetXConverter>(sp_std::marker::PhantomData<AssetXConverter>);
537
impl<AssetXConverter> sp_runtime::traits::Convert<CurrencyId, Option<Location>>
538
	for CurrencyIdToLocation<AssetXConverter>
539
where
540
	AssetXConverter: MaybeEquivalence<Location, AssetId>,
541
{
542
4
	fn convert(currency: CurrencyId) -> Option<Location> {
543
4
		match currency {
544
			CurrencyId::SelfReserve => {
545
1
				let multi: Location = SelfReserve::get();
546
1
				Some(multi)
547
			}
548
3
			CurrencyId::ForeignAsset(asset) => AssetXConverter::convert_back(&asset),
549
			CurrencyId::Erc20 { contract_address } => {
550
				let mut location = Erc20XcmBridgePalletLocation::get();
551
				location
552
					.push_interior(Junction::AccountKey20 {
553
						key: contract_address.0,
554
						network: None,
555
					})
556
					.ok();
557
				Some(location)
558
			}
559
		}
560
4
	}
561
}
562

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

            
579
}
580

            
581
// 1 WND/ROC should be enough
582
parameter_types! {
583
	pub MaxHrmpRelayFee: Asset = (Location::parent(), 1_000_000_000_000u128).into();
584
}
585

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

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

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

            
612
impl UtilityEncodeCall for Transactors {
613
16
	fn encode_call(self, call: UtilityAvailableCalls) -> Vec<u8> {
614
16
		match self {
615
16
			Transactors::Relay => pallet_xcm_transactor::Pallet::<Runtime>::encode_call(
616
16
				pallet_xcm_transactor::Pallet(sp_std::marker::PhantomData::<Runtime>),
617
16
				call,
618
16
			),
619
16
		}
620
16
	}
621
}
622

            
623
impl XcmTransact for Transactors {
624
16
	fn destination(self) -> Location {
625
16
		match self {
626
16
			Transactors::Relay => Location::parent(),
627
16
		}
628
16
	}
629
}
630

            
631
pub type DerivativeAddressRegistrationOrigin =
632
	EitherOfDiverse<EnsureRoot<AccountId>, governance::custom_origins::GeneralAdmin>;
633

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

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

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

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

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

            
689
pub struct EvmForeignAssetIdFilter;
690
impl frame_support::traits::Contains<AssetId> for EvmForeignAssetIdFilter {
691
40
	fn contains(asset_id: &AssetId) -> bool {
692
40
		use xcm_primitives::AssetTypeGetter as _;
693
40
		// We should return true only if the AssetId doesn't exist in AssetManager
694
40
		AssetManager::get_asset_type(*asset_id).is_none()
695
40
	}
696
}
697

            
698
pub type ForeignAssetManagerOrigin = EitherOfDiverse<
699
	EnsureRoot<AccountId>,
700
	EitherOfDiverse<
701
		pallet_collective::EnsureProportionMoreThan<AccountId, OpenTechCommitteeInstance, 5, 9>,
702
		EitherOf<
703
			governance::custom_origins::GeneralAdmin,
704
			governance::custom_origins::FastGeneralAdmin,
705
		>,
706
	>,
707
>;
708

            
709
impl pallet_moonbeam_foreign_assets::Config for Runtime {
710
	type AccountIdToH160 = AccountIdToH160;
711
	type AssetIdFilter = EvmForeignAssetIdFilter;
712
	type EvmRunner = EvmRunnerPrecompileOrEthXcm<MoonbeamCall, Self>;
713
	type ForeignAssetCreatorOrigin = ForeignAssetManagerOrigin;
714
	type ForeignAssetFreezerOrigin = ForeignAssetManagerOrigin;
715
	type ForeignAssetModifierOrigin = ForeignAssetManagerOrigin;
716
	type ForeignAssetUnfreezerOrigin = ForeignAssetManagerOrigin;
717
	type OnForeignAssetCreated = ();
718
	type MaxForeignAssets = ConstU32<256>;
719
	type RuntimeEvent = RuntimeEvent;
720
	type WeightInfo = moonbase_weights::pallet_moonbeam_foreign_assets::WeightInfo<Runtime>;
721
	type XcmLocationToH160 = LocationToH160;
722
}
723

            
724
pub struct AssetFeesFilter;
725
impl frame_support::traits::Contains<Location> for AssetFeesFilter {
726
	fn contains(location: &Location) -> bool {
727
		location.parent_count() > 0
728
			&& location.first_interior() != Erc20XcmBridgePalletLocation::get().first_interior()
729
	}
730
}
731

            
732
pub type AddAndEditSupportedAssetOrigin = EitherOfDiverse<
733
	EnsureRoot<AccountId>,
734
	EitherOfDiverse<
735
		pallet_collective::EnsureProportionMoreThan<AccountId, OpenTechCommitteeInstance, 5, 9>,
736
		EitherOf<
737
			governance::custom_origins::GeneralAdmin,
738
			governance::custom_origins::FastGeneralAdmin,
739
		>,
740
	>,
741
>;
742

            
743
pub type RemoveSupportedAssetOrigin = EitherOfDiverse<
744
	EnsureRoot<AccountId>,
745
	pallet_collective::EnsureProportionMoreThan<AccountId, OpenTechCommitteeInstance, 5, 9>,
746
>;
747

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

            
767
#[cfg(feature = "runtime-benchmarks")]
768
mod testing {
769
	use super::*;
770
	use xcm_builder::WithLatestLocationConverter;
771

            
772
	/// This From exists for benchmarking purposes. It has the potential side-effect of calling
773
	/// AssetManager::set_asset_type_asset_id() and should NOT be used in any production code.
774
	impl From<Location> for CurrencyId {
775
		fn from(location: Location) -> CurrencyId {
776
			use xcm_primitives::AssetTypeGetter;
777

            
778
			// If it does not exist, for benchmarking purposes, we create the association
779
			let asset_id = if let Some(asset_id) =
780
				AsAssetType::<AssetId, AssetType, AssetManager>::convert_location(&location)
781
			{
782
				asset_id
783
			} else {
784
				let asset_type = AssetType::Xcm(
785
					WithLatestLocationConverter::convert(&location).expect("convert to v3"),
786
				);
787
				let asset_id: AssetId = asset_type.clone().into();
788
				AssetManager::set_asset_type_asset_id(asset_type, asset_id);
789
				asset_id
790
			};
791

            
792
			CurrencyId::ForeignAsset(asset_id)
793
		}
794
	}
795
}