astar_runtime/
xcm_config.rs

1// This file is part of Astar.
2
3// Copyright (C) Stake Technologies Pte.Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later
5
6// Astar is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// Astar is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with Astar. If not, see <http://www.gnu.org/licenses/>.
18
19use super::{
20    AccountId, AllPalletsWithSystem, AssetId, Assets, AstarAssetLocationIdConverter, Balance,
21    Balances, DealWithFees, MessageQueue, ParachainInfo, ParachainSystem, PolkadotXcm, Runtime,
22    RuntimeCall, RuntimeEvent, RuntimeOrigin, TreasuryAccountId, XcAssetConfig, XcmWeightToFee,
23    XcmpQueue,
24};
25use crate::weights;
26use frame_support::{
27    parameter_types,
28    traits::{ConstU32, Contains, Everything, Nothing},
29    weights::Weight,
30};
31use frame_system::EnsureRoot;
32use sp_runtime::traits::{Convert, MaybeEquivalence};
33
34// Polkadot imports
35use cumulus_primitives_core::{AggregateMessageOrigin, ParaId};
36use frame_support::traits::{Disabled, TransformOrigin};
37use parachains_common::message_queue::ParaIdToSibling;
38use polkadot_runtime_common::xcm_sender::NoPriceForMessageDelivery;
39use xcm::latest::prelude::*;
40use xcm_builder::{
41    Account32Hash, AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom,
42    AllowUnpaidExecutionFrom, ConvertedConcreteId, EnsureXcmOrigin, FrameTransactionalProcessor,
43    FungibleAdapter, FungiblesAdapter, IsConcrete, NoChecking, ParentAsSuperuser, ParentIsPreset,
44    RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia,
45    SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit,
46    UsingComponents, WeightInfoBounds,
47};
48use xcm_executor::{
49    traits::{JustTry, WithOriginFilter},
50    XcmExecutor,
51};
52
53// ORML imports
54use orml_xcm_support::DisabledParachainFee;
55
56// Astar imports
57use astar_primitives::xcm::{
58    AbsoluteAndRelativeReserveProvider, AccountIdToMultiLocation, AllowTopLevelPaidExecutionFrom,
59    FixedRateOfForeignAsset, ReserveAssetFilter, XcmFungibleFeeHandler,
60};
61
62parameter_types! {
63    pub RelayNetwork: Option<NetworkId> = Some(NetworkId::Polkadot);
64    pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into();
65    pub UniversalLocation: InteriorLocation =
66    [GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(ParachainInfo::parachain_id().into())].into();
67    pub AstarLocation: Location = Here.into_location();
68    pub DummyCheckingAccount: AccountId = PolkadotXcm::check_account();
69}
70
71/// Type for specifying how a `Location` can be converted into an `AccountId`. This is used
72/// when determining ownership of accounts for asset transacting and when attempting to use XCM
73/// `Transact` in order to determine the dispatch Origin.
74pub type LocationToAccountId = (
75    // The parent (Relay-chain) origin converts to the default `AccountId`.
76    ParentIsPreset<AccountId>,
77    // Sibling parachain origins convert to AccountId via the `ParaId::into`.
78    SiblingParachainConvertsVia<polkadot_parachain::primitives::Sibling, AccountId>,
79    // Straight up local `AccountId32` origins just alias directly to `AccountId`.
80    AccountId32Aliases<RelayNetwork, AccountId>,
81    // Derives a private `Account32` by hashing `("multiloc", received multilocation)`
82    Account32Hash<RelayNetwork, AccountId>,
83);
84
85/// Means for transacting the native currency on this chain.
86pub type CurrencyTransactor = FungibleAdapter<
87    // Use this currency:
88    Balances,
89    // Use this currency when it is a fungible asset matching the given location or name:
90    IsConcrete<AstarLocation>,
91    // Convert an XCM Location into a local account id:
92    LocationToAccountId,
93    // Our chain's account ID type (we can't get away without mentioning it explicitly):
94    AccountId,
95    // We don't track any teleports of `Balances`.
96    (),
97>;
98
99/// Means for transacting assets besides the native currency on this chain.
100pub type FungiblesTransactor = FungiblesAdapter<
101    // Use this fungibles implementation:
102    Assets,
103    // Use this currency when it is a fungible asset matching the given location or name:
104    ConvertedConcreteId<AssetId, Balance, AstarAssetLocationIdConverter, JustTry>,
105    // Convert an XCM Location into a local account id:
106    LocationToAccountId,
107    // Our chain's account ID type (we can't get away without mentioning it explicitly):
108    AccountId,
109    // We don't support teleport so no need to check any assets.
110    NoChecking,
111    // We don't support teleport so this is just a dummy account.
112    DummyCheckingAccount,
113>;
114
115/// Means for transacting assets on this chain.
116pub type AssetTransactors = (CurrencyTransactor, FungiblesTransactor);
117
118/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance,
119/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can
120/// biases the kind of local `Origin` it will become.
121pub type XcmOriginToTransactDispatchOrigin = (
122    // Sovereign account converter; this attempts to derive an `AccountId` from the origin location
123    // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for
124    // foreign chains who want to have a local sovereign account on this chain which they control.
125    SovereignSignedViaLocation<LocationToAccountId, RuntimeOrigin>,
126    // Native converter for Relay-chain (Parent) location; will convert to a `Relay` origin when
127    // recognised.
128    RelayChainAsNative<RelayChainOrigin, RuntimeOrigin>,
129    // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when
130    // recognised.
131    SiblingParachainAsNative<cumulus_pallet_xcm::Origin, RuntimeOrigin>,
132    // Superuser converter for the Relay-chain (Parent) location. This will allow it to issue a
133    // transaction from the Root origin.
134    ParentAsSuperuser<RuntimeOrigin>,
135    // Xcm origins can be represented natively under the Xcm pallet's Xcm origin.
136    pallet_xcm::XcmPassthrough<RuntimeOrigin>,
137    // Native signed account converter; this just converts an `AccountId32` origin into a normal
138    // `Origin::Signed` origin of the same 32-byte value.
139    SignedAccountId32AsNative<RelayNetwork, RuntimeOrigin>,
140);
141
142parameter_types! {
143    // One XCM operation is 1_000_000_000 weight - almost certainly a conservative estimate.
144    // For the PoV size, we estimate 4 kB per instruction. This will be changed when we benchmark the instructions.
145    pub UnitWeightCost: Weight = Weight::from_parts(1_000_000_000, 4 * 1024);
146    pub const MaxInstructions: u32 = 100;
147}
148
149pub struct ParentOrParentsPlurality;
150impl Contains<Location> for ParentOrParentsPlurality {
151    fn contains(location: &Location) -> bool {
152        matches!(location.unpack(), (1, []) | (1, [Plurality { .. }]))
153    }
154}
155
156/// A call filter for the XCM Transact instruction. This is a temporary measure until we properly
157/// account for proof size weights.
158pub struct SafeCallFilter;
159impl SafeCallFilter {
160    // 1. RuntimeCall::EVM(..) & RuntimeCall::Ethereum(..) have to be prohibited since we cannot measure PoV size properly
161    // 2. RuntimeCall::Contracts(..) can be allowed, but it hasn't been tested properly yet.
162
163    /// Checks whether the base (non-composite) call is allowed to be executed via `Transact` XCM instruction.
164    pub fn allow_base_call(call: &RuntimeCall) -> bool {
165        match call {
166            RuntimeCall::System(..)
167            | RuntimeCall::Identity(..)
168            | RuntimeCall::Balances(..)
169            | RuntimeCall::Vesting(..)
170            | RuntimeCall::DappStaking(..)
171            | RuntimeCall::Assets(..)
172            | RuntimeCall::PolkadotXcm(..)
173            | RuntimeCall::Session(..)
174            | RuntimeCall::Proxy(
175                pallet_proxy::Call::add_proxy { .. }
176                | pallet_proxy::Call::remove_proxy { .. }
177                | pallet_proxy::Call::remove_proxies { .. }
178                | pallet_proxy::Call::create_pure { .. }
179                | pallet_proxy::Call::kill_pure { .. }
180                | pallet_proxy::Call::announce { .. }
181                | pallet_proxy::Call::remove_announcement { .. }
182                | pallet_proxy::Call::reject_announcement { .. },
183            )
184            | RuntimeCall::Multisig(
185                pallet_multisig::Call::approve_as_multi { .. }
186                | pallet_multisig::Call::cancel_as_multi { .. },
187            ) => true,
188            _ => false,
189        }
190    }
191    /// Checks whether composite call is allowed to be executed via `Transact` XCM instruction.
192    ///
193    /// Each composite call's subcalls are checked against base call filter. No nesting of composite calls is allowed.
194    pub fn allow_composite_call(call: &RuntimeCall) -> bool {
195        match call {
196            RuntimeCall::Proxy(pallet_proxy::Call::proxy { call, .. }) => {
197                Self::allow_base_call(call)
198            }
199            RuntimeCall::Proxy(pallet_proxy::Call::proxy_announced { call, .. }) => {
200                Self::allow_base_call(call)
201            }
202            RuntimeCall::Utility(pallet_utility::Call::batch { calls, .. }) => {
203                calls.iter().all(|call| Self::allow_base_call(call))
204            }
205            RuntimeCall::Utility(pallet_utility::Call::batch_all { calls, .. }) => {
206                calls.iter().all(|call| Self::allow_base_call(call))
207            }
208            RuntimeCall::Utility(pallet_utility::Call::as_derivative { call, .. }) => {
209                Self::allow_base_call(call)
210            }
211            RuntimeCall::Multisig(pallet_multisig::Call::as_multi_threshold_1 { call, .. }) => {
212                Self::allow_base_call(call)
213            }
214            RuntimeCall::Multisig(pallet_multisig::Call::as_multi { call, .. }) => {
215                Self::allow_base_call(call)
216            }
217            _ => false,
218        }
219    }
220}
221
222impl Contains<RuntimeCall> for SafeCallFilter {
223    fn contains(call: &RuntimeCall) -> bool {
224        Self::allow_base_call(call) || Self::allow_composite_call(call)
225    }
226}
227
228pub type XcmBarrier = (
229    TakeWeightCredit,
230    AllowTopLevelPaidExecutionFrom<Everything>,
231    // Parent and its plurality get free execution
232    AllowUnpaidExecutionFrom<ParentOrParentsPlurality>,
233    // Expected responses are OK.
234    AllowKnownQueryResponses<PolkadotXcm>,
235    // Subscriptions for version tracking are OK.
236    AllowSubscriptionsFrom<Everything>,
237);
238
239// Used to handle XCM fee deposit into treasury account
240pub type AstarXcmFungibleFeeHandler = XcmFungibleFeeHandler<
241    AccountId,
242    ConvertedConcreteId<AssetId, Balance, AstarAssetLocationIdConverter, JustTry>,
243    Assets,
244    TreasuryAccountId,
245>;
246
247pub struct XcmConfig;
248impl xcm_executor::Config for XcmConfig {
249    type RuntimeCall = RuntimeCall;
250    type XcmSender = XcmRouter;
251    type AssetTransactor = AssetTransactors;
252    type OriginConverter = XcmOriginToTransactDispatchOrigin;
253    type IsReserve = ReserveAssetFilter;
254    type IsTeleporter = ();
255    type UniversalLocation = UniversalLocation;
256    type Barrier = XcmBarrier;
257    type Weigher = Weigher;
258    type Trader = (
259        UsingComponents<XcmWeightToFee, AstarLocation, AccountId, Balances, DealWithFees>,
260        FixedRateOfForeignAsset<XcAssetConfig, AstarXcmFungibleFeeHandler>,
261    );
262    type ResponseHandler = PolkadotXcm;
263    type AssetTrap = PolkadotXcm;
264    type AssetClaims = PolkadotXcm;
265    type SubscriptionService = PolkadotXcm;
266
267    type PalletInstancesInfo = AllPalletsWithSystem;
268    type MaxAssetsIntoHolding = ConstU32<64>;
269    type AssetLocker = ();
270    type AssetExchanger = ();
271    type FeeManager = ();
272    type MessageExporter = ();
273    type UniversalAliases = Nothing;
274    type CallDispatcher = WithOriginFilter<SafeCallFilter>;
275    type SafeCallFilter = SafeCallFilter;
276    type Aliasers = Nothing;
277    type TransactionalProcessor = FrameTransactionalProcessor;
278
279    type HrmpNewChannelOpenRequestHandler = ();
280    type HrmpChannelAcceptedHandler = ();
281    type HrmpChannelClosingHandler = ();
282    type XcmRecorder = PolkadotXcm;
283    type XcmEventEmitter = PolkadotXcm;
284}
285
286/// Local origins on this chain are allowed to dispatch XCM sends/executions.
287pub type LocalOriginToLocation = SignedToAccountId32<RuntimeOrigin, AccountId, RelayNetwork>;
288
289/// The means for routing XCM messages which are not for local execution into the right message
290/// queues.
291pub type XcmRouter = (
292    // Two routers - use UMP to communicate with the relay chain:
293    cumulus_primitives_utility::ParentAsUmp<ParachainSystem, PolkadotXcm, ()>,
294    // ..and XCMP to communicate with the sibling chains.
295    XcmpQueue,
296);
297
298pub type Weigher =
299    WeightInfoBounds<weights::xcm::XcmWeight<Runtime, RuntimeCall>, RuntimeCall, MaxInstructions>;
300
301impl pallet_xcm::Config for Runtime {
302    const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100;
303
304    type RuntimeEvent = RuntimeEvent;
305    type SendXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
306    type XcmRouter = XcmRouter;
307    type ExecuteXcmOrigin = EnsureXcmOrigin<RuntimeOrigin, LocalOriginToLocation>;
308    type XcmExecuteFilter = Nothing;
309    type XcmExecutor = XcmExecutor<XcmConfig>;
310    type XcmTeleportFilter = Nothing;
311    type XcmReserveTransferFilter = Everything;
312    type Weigher = Weigher;
313    type UniversalLocation = UniversalLocation;
314    type RuntimeOrigin = RuntimeOrigin;
315    type RuntimeCall = RuntimeCall;
316    type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; // TODO:OR should we keep this at 2?
317
318    type Currency = Balances;
319    type CurrencyMatcher = ();
320    type TrustedLockers = ();
321    type SovereignAccountOf = LocationToAccountId;
322    type MaxLockers = ConstU32<0>;
323    type WeightInfo = weights::pallet_xcm::SubstrateWeight<Runtime>;
324    type MaxRemoteLockConsumers = ConstU32<0>;
325    type RemoteLockConsumerIdentifier = ();
326    type AdminOrigin = EnsureRoot<AccountId>;
327    type AuthorizedAliasConsideration = Disabled;
328}
329
330impl cumulus_pallet_xcm::Config for Runtime {
331    type RuntimeEvent = RuntimeEvent;
332    type XcmExecutor = XcmExecutor<XcmConfig>;
333}
334
335impl cumulus_pallet_xcmp_queue::Config for Runtime {
336    type RuntimeEvent = RuntimeEvent;
337    type ChannelInfo = ParachainSystem;
338    type VersionWrapper = PolkadotXcm;
339    type XcmpQueue = TransformOrigin<MessageQueue, AggregateMessageOrigin, ParaId, ParaIdToSibling>;
340    type MaxInboundSuspended = ConstU32<1_000>;
341    type MaxActiveOutboundChannels = ConstU32<128>;
342    type MaxPageSize = ConstU32<{ 128 * 1024 }>;
343    type ControllerOrigin = EnsureRoot<AccountId>;
344    type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin;
345    type PriceForSiblingDelivery = NoPriceForMessageDelivery<ParaId>;
346    type WeightInfo = cumulus_pallet_xcmp_queue::weights::SubstrateWeight<Runtime>;
347}
348
349parameter_types! {
350    /// The absolute location in perspective of the whole network.
351    pub AstarLocationAbsolute: Location = Location {
352        parents: 1,
353        interior: Parachain(ParachainInfo::parachain_id().into()).into()
354
355    };
356    /// Max asset types for one cross-chain transfer. `2` covers all current use cases.
357    /// Can be updated with extra test cases in the future if needed.
358    pub const MaxAssetsForTransfer: usize = 2;
359}
360
361/// Convert `AssetId` to optional `Location`. The impl is a wrapper
362/// on `ShidenAssetLocationIdConverter`.
363pub struct AssetIdConvert;
364impl Convert<AssetId, Option<Location>> for AssetIdConvert {
365    fn convert(asset_id: AssetId) -> Option<Location> {
366        AstarAssetLocationIdConverter::convert_back(&asset_id)
367    }
368}
369
370impl orml_xtokens::Config for Runtime {
371    type Balance = Balance;
372    type CurrencyId = AssetId;
373    type CurrencyIdConvert = AssetIdConvert;
374    type AccountIdToLocation = AccountIdToMultiLocation;
375    type SelfLocation = AstarLocation;
376    type XcmExecutor = XcmExecutor<XcmConfig>;
377    type Weigher = Weigher;
378    type BaseXcmWeight = UnitWeightCost;
379    type UniversalLocation = UniversalLocation;
380    type MaxAssetsForTransfer = MaxAssetsForTransfer;
381    // Default impl. Refer to `orml-xtokens` docs for more details.
382    type MinXcmFee = DisabledParachainFee;
383    type LocationsFilter = Everything;
384    type ReserveProvider = AbsoluteAndRelativeReserveProvider<AstarLocationAbsolute>;
385    type RateLimiter = ();
386    type RateLimiterId = ();
387}