Executor Config

As previously mentioned, the xcm-executor is a Cross-Consensus Virtual Machine(XCVM) implementation. It provides an opinionated interpretation and execution of XCMs. Each chain that uses the xcm-executor, can configure it for their use case. In this chapter we will go over this configuration, explain each config item and give some examples of the tools and types that can be used to configure these items.

XCM Executor Configuration

Below we list the Config trait of the xcm-executor. The Config trait expects multiple associated types. Each type has a trait bound which the concrete type must implement. Some of these types will use a default implementation in most situations (e.g. RuntimeCall). Other types have a default implementation specified by the unit type (). Most types you'll want to carefully choose which implementation they get. For most of these types there are pre-defined solutions and building blocks you can use and adapt to your scenario. These solutions are listed in the xcm-builder folder.

We will now explain each type and go over some of the implementations of the type:

/// The trait to parameterize the `XcmExecutor`.
pub trait Config {
	type RuntimeCall: Parameter + Dispatchable<PostInfo = PostDispatchInfo> + GetDispatchInfo;
	type XcmSender: SendXcm;
	type AssetTransactor: TransactAsset;
	type OriginConverter: ConvertOrigin<<Self::RuntimeCall as Dispatchable>::RuntimeOrigin>;
	type IsReserve: ContainsPair<MultiAsset, MultiLocation>;
	type IsTeleporter: ContainsPair<MultiAsset, MultiLocation>;
	type UniversalLocation: Get<InteriorMultiLocation>;
	type Barrier: ShouldExecute;
	type Weigher: WeightBounds<Self::RuntimeCall>;
	type Trader: WeightTrader;
	type ResponseHandler: OnResponse;
	type AssetTrap: DropAssets;
	type AssetClaims: ClaimAssets;
	type AssetLocker: AssetLock;
	type AssetExchanger: AssetExchange;
	type SubscriptionService: VersionChangeNotifier;
	type PalletInstancesInfo: PalletsInfoAccess;
	type MaxAssetsIntoHolding: Get<u32>;
	type FeeManager: FeeManager;
	type MessageExporter: ExportXcm;
	type UniversalAliases: Contains<(MultiLocation, Junction)>;
	type CallDispatcher: CallDispatcher<Self::RuntimeCall>;
	type SafeCallFilter: Contains<Self::RuntimeCall>;
}

How to use multiple implementations.

Some associated types in the Config trait are highly configurable and in certain cases will have multiple implementations (e.g. Barrier). These implementations are then grouped using a tuple (impl_1, impl_2, ..., impl_n). The execution of the tuple type is sequential, meaning that each item is executed one after another. Each item is checked to see whether it fails to pass, then the next item is checked, and so on. The execution is halted when one of these items returns positive (Ok or true, etc.). The next example of the Barrier type shows how the grouping works (understanding each item in the tuple is not necessary).

pub type Barrier = (
	TakeWeightCredit,
	AllowTopLevelPaidExecutionFrom<Everything>,
	AllowKnownQueryResponses<XcmPallet>,
	AllowSubscriptionsFrom<Everything>,
);

pub struct XcmConfig;
impl xcm_executor::Config for XcmConfig {
    ...
	type Barrier = Barrier;
    ...
}

In the above example, when checking the barrier, we'll first check the TakeWeightCredit type. If it fails, we'll go on to check the AllowTopLevelPaidExecutionFrom and so on until one of them gives a positive. If they all fail, a Barrier error is thrown.

Config Items

We now go over each config item to explain what the associate type does and how it is used in the xcm-executor. Many of these types have pre-defined solutions that can be found in the xcm-builder and a good way to understand these configurations is to look at example configurations. On the bottom of this page we listed some examples.

RuntimeCall

The RuntimeCall type is equal to the RuntimeCall created in the construct_runtime! macro. It is an enum of all the callable functions of each of the implemented pallets.

XcmSender

The XcmSender type implements the SendXcm trait, and defines how the xcm_executor can send XCMs (which transport layer it can use for the XCMs). This type normally implements a tuple for one or more transport layer(s). For example a parachain can implement the XcmSender as:

 (
	// Two routers - use UMP to communicate with the relay chain:
	cumulus_primitives_utility::ParentAsUmp<ParachainSystem, PolkadotXcm, ()>,
	// ..and XCMP to communicate with the sibling chains.
	XcmpQueue,
);

If a runtime does not contain the XcmpQueue pallet as a config item for XcmSender, it will not be able to send messages to other parachains. This can be useful for controlling the destinations that an XCM can be sent to.

AssetTransactor

The AssetTransactor type implements the TransactAsset trait and defines how the xcm-executor can convert MultiAssets from and to on chain assets and how to transfer these assets between accounts, or from and to the holding register. As chains can support different types of currencies (native tokens), fungibles and non-fungibles, we can configure the AssetTransactor in different ways, depending on the chains implementation fo these types. Three default implementations are provided in the xcm-builder, namely the CurrencyAdapter, FungiblesAdapter and NonFungiblesAdapter.

OriginConverter

The OriginConverter type implements the ConvertOrigin trait and defines how the xcm-executor can convert a MultiLocation into a RuntimeOrigin. Most xcm-executors take multiple implementations in a tuple for this configuration as there are many different MLs we would like to convert. When multiple OriginConverters conflict, the OriginKind that is passed to the convert_origin function is used to distingues which OriginConverter to use. There are four different OriginKinds :

pub enum OriginKind {
	Native,
	SovereignAccount,
	Superuser,
	Xcm,
}

An example of the use of OriginKinds are the SovereignSignedViaLocation and SignedAccountId32AsNative OriginConverters (defined in xcm-builder). The first converts an sovereign account into a Signed RuntimeOrigin (uses SovereignAccount OriginKind) while the second converts a local native account into a Signed RuntimeOrigin (uses Native OriginKind).

pub type SovereignAccountOf = AccountId32Aliases<ThisNetwork, AccountId>;
(
	// A `Signed` origin of the sovereign account that the original location controls.
	SovereignSignedViaLocation<SovereignAccountOf, RuntimeOrigin>,
	// The AccountId32 location type can be expressed natively as a `Signed` origin.
	SignedAccountId32AsNative<ThisNetwork, RuntimeOrigin>,
);

IsReserve

The IsReserve type must be set to specify which <MultiAsset, MultiLocation> pair we trust to deposit reserve assets on our chain. We can also use the unit type () to block ReserveAssetDeposited instructions. An example implementation is the NativeAsset struct, that accepts an asset iff it is a native asset.

IsTeleporter

The IsTeleporter type must be set to specify which <MultiAsset, MultiLocation> pair we trust to teleport assets to our chain. We can also use the unit type () to block ReceiveTeleportedAssets instruction. An example implementation is the NativeAsset struct, that accepts an asset iff it is a native asset.

UniversalLocation

The UniversalLocation type describes the location of the runtime implementing the xcm-executor in the consensus universe. Below we give some examples of UniversalLocation implementations.

//Polkadot
X1(GlobalConsensus(NetworkId::Polkadot))
//Kusama
X1(GlobalConsensus(NetworkId::Kusama))
//Statemint
X2(GlobalConsensus(NetworkId::Polkadot), Parachain(1000))

Barrier

Before any XCMs are executed in the XCM executor, they need to pass the Barrier. The Barrier type implements the ShouldExecute trait and can be seen as the firewall of the xcm-executor. Each time the xcm-executor receives an XCM, it check with the barrier if the XCM should be executed. We can also define multiple barriers for our Barrier type by using a tuple. During execution, each barrier is checks, and if one of them succeed, the XCM is executed. Example of a Barrier implementations is AllowTopLevelPaidExecutionFrom<T> that accepts the XCM if the T contains the origin of the XCM and the XCM contains the BuyExecution instruction. To accept all XCMs that pay for execution we could set the barrier to AllowTopLevelPaidExecutionFrom<Everything>. There are multiple pre-defined barrier implementations in the xcm-builder.

Weigher

The Weigher is responsible for weighing full XCMs and individual instructions. This weight is calculated before the XCM execution, and this calculated weight is checked against the weight_limit. If the weight is more than weight_limit, the xcm will not be executed. The weight is also passed to each Barrier, as certain barriers execute weight-based checks. After the execution of the XCM, unused weight is refunded (if possible). There are pre-defined Weigher solutions in the xcm-builder. The most used is the FixedWeightBounds:

// BaseXcmWeight is a const weight.
FixedWeightBounds<BaseXcmWeight, RuntimeCall, MaxInstructions>;

Note: More information about weight.

Trader

The Trader type is responsible for buying weight in the BuyExecution instruction using assets in the holding register and to refund unspend weight. One of the first implementations of the Trader is defined in the xcm-builder, namely the UsingComponents trader.

ResponseHandler

The ResponseHandler type is responsible for handling the QueryResponse instructions. A ResponseHandler implementation has to implement the OnResponse trait. One of the implementations of the ResponseHandler is the pallet-xcm. This will be the main implementation for most FRAME-based systems that implement the XCM-executor. Another option is to use the unit type () if you do not want to support QueryResponse.

AssetTrap

The AssetTrap type is responsible for handling the funds left over in holding after the execution of the XCM. The assets are stored in the AssetTrap and can be claimed using the ClaimAsset instruction. One of the implementations of the AssetTrap type is the pallet-xcm. Another option is to use the unit type () if you do not want to support asset trapping. In this case, the assets that are left in holding are burned.

AssetClaims

The AssetClaims type is responsible for claiming trapped assets. It is during execution of the ClaimAsset instruction. One of the implementations of the AssetClaims type is the pallet-xcm. Another option is to use the unit type () if you do not want to support asset claiming.

AssetLocker

The AssetLocker type is responsible with handling locking and unlocking assets. One of the implementations of the AssetLocker type is the pallet-xcm. Another option is to use the unit type () if you do not want to support asset locking.

AssetExchanger

The AssetExchanger type implements the AssetExchange trait and handles the exchange of assets for the ExchangeAsset instruction. An option is to use the unit type () if you do not want to support asset exchanging.

SubscriptionService

The SubscriptionService type implements the VersionChangeNotifier trait and is used for the execution of the (Un)SubscribeVersion instructions. When a chain receives the SubscribeVersion instruction, the SubscriptionService should send back a QueryResponse with the XCM version that the chain uses. One of the implementations of the SubscriptionService is the pallet-xcm. This will be the main implementation for most FRAME-based systems that implement the XCM-executor.

PalletInstancesInfo

The PalletInstancesInfo type implements the PalletsInfoAccess trait and is used in the QueryPallet and ExpectPallet instructions. It supplies the information of all the pallets in the Runtime, and is therefore FRAME specific. The unit type () can be used if you do not want to support pallet information.

MaxAssetsIntoHolding

The MaxAssetsIntoHolding type is used to set a limit on the number of assets in the Holding Register. In the worse case, the Holding Register may contain up to twice as many assets as this limit.

FeeManager

The FeeManager type is used to manage what happens with the fees that need to be paid for certain XCM instructions. A FeeManager implementation implements the FeeManager trait. The FeeManager determines if fees should be paid (or if they are waived) and what to do with the paid fees. The unit type () can be used if you want to waive every fee.

MessageExporter

The MessageExporter type implements the ExportXcm trait and is used to export a message to another consensus system. The MessageExporter is different from the XcmSender. The MessageExporter is able to spoof the origin of the message, meaning it can represent a different origin then the local (i.e. the caller chain's) location. The MessageExporter will mainly be used to send XCMs over bridges. For a more in depth explanation, see the ExportXcm trait. The unit type () can be used if you do not want to support XCM exporting.

UniversalAliases

The UniversalAliases type is used to list the origin locations and specific universal junctions to which they are allowed to elevate themselves. UniversalAliases is used in the UniversalOrigin instruction. To not allow any alliasing of origins, Nothing can be used.

CallDispatcher

The CallDispatcher type is used by xcm-executor to dispatch calls that are passed in the Transact instruction with the given origin. When no special call dispatcher is required, this can be set to the same type as RuntimeCall. However, CallDispatcher can be used to customize call dispatch, such as adapting the origin based on the call or modifying the call.

SafeCallFilter

The SafeCallFilter type is used by the xcm-executor to whitelist calls that can be made in the Transact instruction. This is a temporary measure until proof size weights for XCM instructions are properly account for. If you want to allow all calls in Tansact, use Everything.

What Next

Check out the Kusama, Statemine, or Trappist for examples of how to implement the xcm-executor config.