Skip to content

Using the XCM-Transactor Pallet for Remote Executions

XCM-Transactor Precompile Contracts Banner

Introduction

XCM messages are comprised of a series of instructions that are executed by the Cross-Consensus Virtual Machine (XCVM). Combinations of these instructions result in predetermined actions such as cross-chain token transfers and, more interestingly, remote cross-chain execution.

Nevertheless, building an XCM message from scratch is somewhat tricky. Moreover, XCM messages are sent to other participants in the ecosystem from the root account (that is, SUDO or through a democratic vote), which is not ideal for projects that want to leverage remote cross-chain calls via a simple transaction.

To overcome these issues, developers can leverage wrapper functions/pallets to use XCM features on Polkadot/Kusama, such as the XCM-Transactor pallet. In addition, the XCM-Transactor pallet allows users to perform remote cross-chain calls from an account derivated from the sovereign account (called derivative account), so that they can be easily executed with a simple transaction.

This guide will show you how to use the XCM-Transactor pallet to send XCM messages from a Moonbeam-based network to other chains in the ecosystem (relay chain/parachains). In addition, you'll also learn how to use the XCM-Transactor precompile to perform the same actions via the Ethereum API.

Note that there are still limitations in what you can remotely execute through XCM messages.

Developers must understand that sending incorrect XCM messages can result in the loss of funds. Consequently, it is essential to test XCM features on a TestNet before moving to a production environment.

Relevant XCM Definitions

  • Sovereign account — an account that each chain in the ecosystem has in the other chains. The account is owned by root and can only be used through SUDO (if available) or democracy (technical committee or referenda). The sovereign account normally signs XCM messages in other chains in the ecosystem
  • Multilocation — a way to specify a point in the entire relay chain/parachain ecosystem from a given origin, either relative or absolute. For example, it can be used to specify a specific parachain, asset, account, or even a pallet inside a parachain. In general terms, a multilocation is defined with a parents and an interior. Parents refers to how many "hops" into a parent blockchain you need to take from a given origin. The interior refers to how many fields you need to define the target point. For example, to target a parachain with ID 1000 from another parachain, the multilocation would be { "parents": 1, "interior": { "X1": [{ "Parachain": 1000 }]}}

  • Derivative account — an account derivated from another account using a simple index. The derivation is done by calculating the Blake2 hash of modlpy/utilisuba + originalAddress + index. Because the private key of this account is unknown, transactions must be initiated with the utility.asDerivative method. When transacting through the derivative account, transaction fees are paid by the origin account, but the transaction is dispatched from the derivative account. For example, Alice has a derivative account with an index 0. If she transfers any balance using the asDerivative function, Alice would still pay for transaction fees, but the funds being transferred will be withdrawn from the derivative account at index 0. You can use a script to calculate the derivative account

  • Transact information — relates to extra weight and fee information for the XCM remote execution part of the XCM-Transactor extrinsic. This is needed because the XCM transaction fee is paid by the sovereign account. Therefore, XCM-Transactor calculates what this fee is, and charges the sender of the XCM-Transactor extrinsic the estimated amount in the corresponding XC-20 token, to repay the sovereign account

XCM-Transactor Pallet Interface

The XCM-Transactor pallet provides the following extrinsics (functions):

  • deregister(index) — deregisters a derivative account for a given index. This prevents the previously registered account from using a derivative address for remote execution. This extrinsic is only callable by root, for example, through a democracy proposal
  • register(address, index) — registers a given address as a derivative account at a given index. This extrinsic is only callable by root, for example, through a democracy proposal
  • removeTransactInfo(location) — remove the transact information for a given chain, defined as a multilocation
  • setTransactInfo(location, transactExtraWeight, feePerSecond, maxWeight) — sets the transact information for a given chain, defined as a multilocation. The transact information includes:
    • transactExtraWeight — which is estimated to be at least 10% over what the remote XCM instructions execution uses (WithdrawAsset, BuyExecution, and Transact)
    • feePerSecond — it is the token units per second of XCM execution that will be charged to the sender of the XCM-Transactor extrinsic
    • maxWeight — maximum weight units allowed for the remote XCM execution
  • transactThroughDerivative(destination, index, currencyID, destWeight, innerCall) — sends an XCM message with instructions to remotely execute a given call in the given destination (wrapped with the asDerivative option). The remote call will be signed by the origin parachain sovereign account (who pays the fees), but the transaction is dispatched from the sovereign's derivative account for the given index. The XCM-Transactor pallet calculates the fees for the remote execution and charges the sender of the extrinsic the estimated amount in the corresponding XC-20 token given by the asset ID
  • transactThroughDerivativeMultilocation(destination, index, feeLocation, destWeight, innerCall) — same as transactThroughDerivative, but the remote execution fees are charged in the corresponding XC-20 token given by the asset multilocation
  • transactThroughSovereign(destination, feePayer, feeLocation, destWeight, call, originKind) — sends an XCM message with instructions to remotely execute a given call in the given destination. The remote call will be signed by the origin parachain sovereign account (who pays the fees), but the transaction is dispatched from a given origin. The XCM-Transactor pallet calculates the fees for the remote execution and charges the given account the estimated amount in the corresponding XC-20 token given by the asset multilocation

Where the inputs that need to be provided can be defined as:

  • index — value to be used to calculate the derivative account. In the context of the XCM-Transactor pallet, this is a derivative account of the parachain sovereign account in another chain
  • location — a multilocation representing a chain in the ecosystem. The value is used to set or retrieve the transact information
  • destination — a multilocation representing a chain in the ecosystem where the XCM message is being sent to
  • currencyID — the ID of the currency being used to pay for the remote call execution. Different runtimes have different ways of defining the IDs. In the case of Moonbeam-based networks, SelfReserve refers to the native token, and ForeignAsset refers to the asset ID of the XC-20 (not to be confused with the XC-20 address)
  • destWeight — the maximum amount of execution time you want to provide in the destination chain to execute the XCM message being sent. If not enough weight is provided, the execution of the XCM will fail, and funds might get locked in either the sovereign account or a special pallet. For transacts through derivative, you have to take into account the asDerivative extrinsic as well, but the XCM-Transactor pallet adds the weight for the XCM instructions with the transact information set before. It is essential to correctly set the destination weight to avoid failed XCM executions
  • innerCall — encoded call data of the call that will be executed in the destination chain. This is wrapped with the asDerivative option if transacting through the derivative account
  • feeLocation — a multilocation representing the currency being used to pay for the remote call execution
  • feePayer — the address that will pay for the remote XCM execution in the transact through sovereign extrinsic. The fee is charged in the corresponding XC-20 token
  • call — similar to innerCall, but it is not wrapped with the asDerivative extrinsic
  • originKind — dispatcher of the remote call in the destination chain. There are four types of dispatchers available

The XCM-Transactor pallet provides three read-methods:

  • indexToAccount(index) — returns the origin chain account associated with the given derivative index
  • transactInfoWithWeightLimit(location) — returns the transact information for a given multilocation
  • palletVersion() — returns current pallet version from storage

Building an XCM with the XCM-Transactor Pallet

This guide covers building an XCM message for remote executions using the XCM-Transactor pallet, specifically with the transactThroughDerivative function. The steps to use the transactThroughDerivativeMultilocation function are identical, but instead of the currency ID, you specify a multilocation for the fee token.

Note

You need to ensure that the call you are going to execute remotely is allowed in the destination chain!

Checking Prerequisites

To be able to send the extrinsics in Polkadot.js Apps, you need to have:

  • An account with funds
  • The account from which you are going to send the XCM through the XCM-Transactor pallet must also be registered in a given index to be able to operate through a derivative account of the sovereign account. The registration is done through the root account (SUDO in Moonbase Alpha), so contact us to get it registered. For this example, Alice's account was registered at index 42
  • Remote calls through XCM-Transactor require the destination chain fee token to pay for that execution. Because the action is initiated in Moonbeam, you'll need to hold its XC-20 representation. For this example, because you are sending an XCM message to the relay chain, you need xcUNIT tokens to pay for the execution fees, which is the Moonbase Alpha representation of the Alphanet relay chain token UNIT. You can acquire some by swapping for DEV tokens (Moonbase Alpha's native token) on Moonbeam-Swap, a demo Uniswap-V2 clone on Moonbase Alpha

Moonbeam Swap xcUNITs

To check your xcUNIT balance, you can add the XC-20 to MetaMask with the following address:

0xFfFFfFff1FcaCBd218EDc0EbA20Fc2308C778080

If you're interested in how the precompile address is calculated, you can check out the following guides:

XCM-Transactor Transact Through Derivative Function

In this example, you'll build an XCM message to execute a remote call in the relay chain from Moonbase Alpha through the transactThroughDerivative function of the XCM-Transactor pallet.

If you've checked the prerequisites, head to the extrinsic page of Polkadot JS Apps and set the following options:

  1. Select the account from which you want to send the XCM. Make sure the account complies with all the prerequisites
  2. Choose the xcmTransactor pallet
  3. Choose the transactThroughDerivative extrinsic
  4. Set the destination to Relay, to target the relay chain
  5. Enter the index of the derivative account you've been registered to. For this example, the index value is 42. Remember that the derivate account depends on the index
  6. Set the currency ID to ForeignAsset. This is because you are not transferring DEV tokens (SelfReserve)
  7. Enter the asset ID. For this example, xcUNIT has an asset id of 42259045809535163221576417993425387648. You can check all available assets IDs in the XC-20 address section
  8. Set the destination weight. The value must include the asDerivative extrinsic as well. However, the weight of the XCM instructions is added by the XCM-Transactor pallet. For this example, 1000000000 is enough
  9. Enter the inner call that will be executed in the destination chain. This is the encoded call data of the pallet, method, and input values to be called. It can be constructed in Polakdot.js Apps (must be connected to the destination chain), or using the Polkadot.js API. For this example, the inner call is 0x04000030fcfb53304c429689c8f94ead291272333e16d77a2560717f3a7a410be9b208070010a5d4e8, which is a simple balance transfer of 1 UNIT to Alice's account in the relay chain. You can decode the call in Polkadot.js Apps
  10. Click the Submit Transaction button and sign the transaction

Note

The encoded call data for the extrinsic configured above is 0x2103002a00018080778c30c20fa2ebc0ed18d2cbca1f00ca9a3b00000000a404000030fcfb53304c429689c8f94ead291272333e16d77a2560717f3a7a410be9b208070010a5d4e8.

XCM-Transactor Transact Through Derivative Extrinsic

Once the transaction is processed, you can check the relevant extrinsics and events in Moonbase Alpha and the relay chain. Note that, in Moonbase Alpha, there is an event associated to the transactThroughDerivative method, but also some xcUNIT tokens are burned to repay the sovereign account for the transactions fees. In the relay chain, the paraInherent.enter extrinsic shows a balance.Transfer event, where 1 UNIT token is transferred to Alice's address. Still, the transaction fees are paid by the Moonbase Alpha sovereign account.

Retrieve Registered Derivative Indexes

To fetch a list of all registered addresses allowed to operate through the Moonbeam-based network sovereign account and their corresponding indexes, head to the Chain State section of Polkadot.js Apps (under the Developer tab). In there, take the following steps:

  1. From the selected state query dropdown, choose xcmTransactor
  2. Select the indexToAccount method
  3. (Optional) Disable/enable the include options slider. This will allow you to query the address authorized for a given index or request all addresses for all registered indexes
  4. If you've enabled the slider, enter the index to query
  5. Send the query by clicking on the + button

Check Registered Derivative Indexes

XCM-Transactor Precompile

The XCM-Transactor precompile contract allows developers to access the XCM-Transactor pallet features through the Ethereum API of Moonbeam-based networks. Similar to other precompile contracts, the XCM-Transactor precompile is located at the following addresses:

0x0000000000000000000000000000000000000806
0x0000000000000000000000000000000000000806
0x0000000000000000000000000000000000000806

The XCM-Transactor Solidity Interface

XcmTransactor.sol is an interface through which developers can interact with the XCM-Transactor pallet using the Ethereum API.

The interface includes the following functions:

  • index_to_account(uint16 index) — read-only function that returns the registered address authorized to operate using a derivative account of the Moonbeam-based network sovereign account for the given index
  • transact_info(Multilocation memory multilocation) — read-only function that, for a given chain defined as a multilocation, returns the transact information

  • fee_per_second(Multilocation memory multilocation) — read-only function that, for a given asset as a multilocation, returns units of token per second of the XCM execution that is charged as the XCM execution fee. This is useful when, for a given chain, there are multiple assets that can be used for fee payment

  • transact_through_derivative(uint8 transactor, uint16 index, address address, uint64 weight, bytes memory inner_call) — function that represents the transactThroughDerivative method described in the previous example. Instead of the currency ID (asset ID), you'll need to provide the assets precompile address for the address of the token that is used for fee payment.
  • transact_through_derivative_multilocation(uint8 transactor, uint16 index, Multilocation memory fee_asset, uint64 weight, bytes memory inner_call) — function that represents the transactThroughDerivativeMultilocation method. It is very similar transact_through_derivative, but you need to provide the asset multilocation of the token that is used for fee payment instead of the XC-20 token address

Building the Precompile Multilocation

In the XCM-Transactor precompile interface, the Multilocation structure is defined as follows:

 struct Multilocation {
    uint8 parents;
    bytes [] interior;
}

Note that each multilocation has a parents element, defined in this case by a uint8, and an array of bytes. Parents refer to how many "hops" in the upwards direction you have to do if you are going through the relay chain. Being a uint8, the normal values you would see are:

Origin Destination Parents Value
Parachain A Parachain A 0
Parachain A Relay Chain 1
Parachain A Parachain B 1

The bytes array (bytes[]) defines the interior and its content within the multilocation. The size of the array defines the interior value as follows:

Array Size Interior Value
[] 0 Here
[XYZ] 1 X1
[XYZ, ABC] 2 X2
[XYZ, ... N] N XN

Note

Interior value Here is often used for the relay chain (either as a destination or to target the relay chain asset).

Suppose the bytes array contains data. Each element's first byte (2 hexadecimal numbers) corresponds to the selector of that XN field. For example:

Byte Value Selector Data Type
0x00 Parachain bytes4
0x01 AccountId32 bytes32
0x02 AccountIndex64 u64
0x03 AccountKey20 bytes20
0x04 PalletInstance byte
0x05 GeneralIndex u128
0x06 GeneralKey bytes[]

Next, depending on the selector and its data type, the following bytes correspond to the actual data being provided. Note that for AccountId32, AccountIndex64, and AccountKey20, the network field seen in the Polkadot.js Apps example is appended at the end. For example:

Selector Data Value Represents
Parachain "0x00+000007E7" Parachain ID 2023
AccountId32 "0x01+AccountId32+00" AccountId32, Network Any
AccountKey20 "0x03+AccountKey20+00" AccountKey20, Network Any
PalletInstance "0x04+03" Pallet Instance 3

Note

The interior data usually needs to be wrapped around quotes. On the contrary, you might get an invalid tuple value error.

The following code snippet goes through some examples of Multilocation structures, as they would need to be fed into the XCM-Transactor precompile functions:

// Multilocation targeting the relay chain asset from a parachain
{
    1, // parents = 1
    [] // interior = here
}

// Multilocation targeting Moonbase Alpha DEV token from another parachain
{
    1, // parents = 1
    // Size of array is 2, meaning is an X2 interior
    [
        "0x00000003E8", // Selector Parachain, ID = 1000 (Moonbase Alpha)
        "0x0403" // Pallet Instance = 3
    ]
}

// Multilocation targeting aUSD asset on Acala
{
    1, // parents = 1
    // Size of array is 1, meaning is an X1 interior
    [
        "0x00000007D0", // Selector Parachain, ID = 2000 (Acala)
        "0x060001" // General Key Selector + Asset Key
    ]
}