XCM Fees on Moonbeam¶
Introduction¶
XCM aims to be a language that communicates ideas between consensus systems. Sending an XCM message consists of a series of instructions that are executed in both the origin and the destination chain. The combination of XCM instructions results in actions such as token transfers. In order to process and execute each XCM instruction, there are typically associated fees that must be paid.
However, XCM is designed to be general, extensible, and efficient so that it remains valuable and future-proof throughout a growing ecosystem. As such, the generality applies to concepts including payments of fees for XCM execution. In Ethereum, fees are baked into the transaction protocol, whereas in the Polkadot ecosystem, each chain has the flexibility to define how XCM fees are handled.
This guide will cover aspects of fee payment, such as who is responsible for paying XCM execution fees, how it is paid for, and how the fees are calculated on Moonbeam.
Note
The following information is provided for general information purposes only. The weight and extrinsic base cost might have changed since the time of writing. Please ensure you check the actual values, and never use the following information for production apps.
Payment of Fees¶
Generally speaking, the fee payment process can be described as follows:
- Some assets need to be provided
- The exchange of assets for computing time (or weight) must be negotiated
- The XCM operations will be performed as instructed, with the provided weight limit or funds available for execution
Each chain can configure what happens with the XCM fees and in which tokens they can be paid (either the native reserve token or an external one). For example, on Polkadot and Kusama, the fees are paid in DOT or KSM (respectively) and given to the validator of the block. On Moonbeam and Moonriver, the XCM execution fees can be paid in the reserve asset (GLMR or MOVR, respectively), but also in assets originated in other chains, and fees are sent to the treasury.
Consider the following scenario: Alice has some DOT on Polkadot, and she wants to transfer it to Alith on Moonbeam. She sends an XCM message with a set of XCM instructions that will retrieve a given amount of DOT from her account on Polkadot and mint them as xcDOT into Alith's account. Part of the instructions are executed on Polkadot, and the other part are executed on Moonbeam.
How does Alice pay Moonbeam to execute these instructions and fulfill her request? Her request is fulfilled through a series of XCM instructions that are included in the XCM message, which enables her to buy execution time minus any related XCM execution fees. The execution time is used to issue and transfer xcDOT, a representation of DOT on Moonbeam. This means that when Alice sends some DOT to Alith's account on Moonbeam, she'll receive a 1:1 representation of her DOT as xcDOT minus any XCM execution fees. Note that in this scenario, XCM execution fees are paid in xcDOT.
The exact process for Alice's transfer is as follows:
- Assets are sent to an account on Polkadot that is owned by Moonbeam, known as the sovereign account. After the assets are received, an XCM message is sent to Moonbeam
- The XCM message in Moonbeam will:
- Mint the corresponding asset representation
- Buy the corresponding execution time
- Use that execution time to deposit the representation (minus fees) to the destination account
XCM Instructions¶
An XCM message is comprised of a series of XCM instructions. As a result, different combinations of XCM instructions result in different actions. For example, to move DOT to Moonbeam, the following XCM instructions are used:
TransferReserveAsset
- gets executed in Polkadot. Moves assets from the origin account and deposits them into a destination account. In this case, the destination account is Moonbeam's sovereign account on Polkadot. It then sends an XCM message to the destination, which is Moonbeam, with the XCM instructions that are to be executedReserveAssetDeposited
- gets executed in Moonbeam. Takes a representation of the assets received in the sovereign account and places them into the holding register, a temporary position in the Cross-Consensus Virtual Machine (XCVM)ClearOrigin
- gets executed in Moonbeam. Ensures that later XCM instructions cannot command the authority of the XCM authorBuyExecution
- gets executed in Moonbeam. Takes the assets from holding to pay for execution fees. The fees to pay are determined by the target chain, which in this case is MoonbeamDepositAsset
- gets executed in Moonbeam. Removes the assets from holding and sends them to a destination account on Moonbeam
To check how the instructions for an XCM message are built to transfer self-reserve assets to a target chain, such as DOT to Moonbeam, you can refer to the X-Tokens Open Runtime Module Library repository (as an example). You'll want to take a look at the transfer_self_reserve_asset
function. You'll notice it calls TransferReserveAsset
and passes in assets
, dest
, and xcm
as parameters. In particular, the xcm
parameter includes the BuyExecution
and DepositAsset
instructions. If you then head over to the Polkadot GitHub repository, you can find the TransferReserveAsset
instruction. The XCM message is constructed by combining the ReserveAssetDeposited
and ClearOrigin
instructions with the xcm
parameter, which as mentioned includes the BuyExecution
and DepositAsset
instructions.
To move xcDOT from Moonbeam back to Polkadot, the instructions that are used are:
WithdrawAsset
- gets executed in Moonbeam. Removes assets and places them into the holding registerInitiateReserveWithdraw
- gets executed in Moonbeam. Removes the assets from holding (essentially burning them) and sends an XCM message to the destination chain starting with theWithdrawAsset
instructionWithdrawAsset
- gets executed in Polkadot. Removes assets and places them into the holding registerClearOrigin
- gets executed in Polkadot. Ensures that later XCM instructions cannot command the authority of the XCM authorBuyExecution
- gets executed in Polkadot. Takes the assets from holding to pay for execution fees. The fees to pay are determined by the target chain, which in this case is PolkadotDepositAsset
- gets executed in Polkadot. Removes the assets from holding and sends them to a destination account on Polkadot
To check how the instructions for an XCM message are built to transfer reserve assets to a target chain, such as xcDOT to Polkadot, you can refer to the X-Tokens Open Runtime Module Library repository. You'll want to take a look at the transfer_to_reserve
function. You'll notice that it calls WithdrawAsset
, then InitiateReserveWithdraw
and passes in assets
, dest
, and xcm
as parameters. In particular, the xcm
parameter includes the BuyExecution
and DepositAsset
instructions. If you then head over to the Polkadot GitHub repository, you can find the InitiateReserveWithdraw
instruction. The XCM message is constructed by combining the WithdrawAsset
and ClearOrigin
instructions with the xcm
parameter, which as mentioned includes the BuyExecution
and DepositAsset
instructions.
Relay Chain XCM Fee Calculation¶
Substrate has introduced a weight system that determines how heavy or, in other words, how expensive from a computational cost perspective an extrinsic is. One unit of weight is defined as one picosecond of execution time. When it comes to paying fees, users will pay a transaction fee based on the weight of the call that is being made, in addition to factors such as network congestion.
The following sections will break down how to calculate XCM fees for Polkadot and Kusama. It's important to note that Kusama, in particular, uses benchmarked data to determine the total weight costs for XCM instructions and that some XCM instructions might include database reads/writes, which add weight to the call.
There are two databases available in Polkadot and Kusama, RocksDB (which is the default) and ParityDB, both of which have their own associated weight costs for each network.
Polkadot¶
As previously mentioned, Polkadot currently uses a fixed amount of weight for all XCM instructions, which is 1,000,000,000
weight units per instruction.
Although Polkadot doesn't currently use database weight units to calculate costs, the weight units for database operations, which have been benchmarked, are shared here for reference.
Database | Read | Write |
---|---|---|
RocksDB (default) | 20,499,000 | 83,471,000 |
ParityDB | 11,826,000 | 38,052,000 |
With the instruction weight cost established, you can calculate the cost of each instruction in DOT.
In Polkadot, the ExtrinsicBaseWeight
is set to 107,648,000
which is mapped to 1/10th of a cent. Where 1 cent is 10^10 / 100
.
Therefore, to calculate the cost of executing an XCM instruction, you can use the following formula:
XCM-DOT-Cost = XCMInstrWeight * DOTWeightToFeeCoefficient
Where DOTWeightToFeeCoefficient
is a constant (map to 1 cent), and can be calculated as:
DOTWeightToFeeCoefficient = 10^10 / ( 10 * 100 * DOTExtrinsicBaseWeight )
Using the actual values:
DOTWeightToFeeCoefficient = 10^10 / ( 10 * 100 * 107648000 )
As a result, DOTWeightToFeeCoefficient
is equal to 0.092895362 Planck-DOT
. Now, you can begin to calculate the final fee in DOT, using DOTWeightToFeeCoefficient
as a constant and TotalWeight
as the variable:
XCM-Planck-DOT-Cost = TotalWeight * DOTWeightToFeeCoefficient
XCM-DOT-Cost = XCM-Planck-DOT-Cost / DOTDecimalConversion
Therefore, the actual calculation for one XCM instruction is:
XCM-Planck-DOT-Cost = 1000000000 * 0.092895362
XCM-DOT-Cost = 92895362 / 10^10
The total cost is 0.0092895362 DOT
per instruction.
As an example, you can calculate the total cost of DOT for sending an XCM message that transfers xcDOT to DOT on Polkadot using the following weights and instruction costs:
Instruction | Weight | Cost |
---|---|---|
WithdrawAsset |
1,000,000,000 | 0.0092895362 DOT |
ClearOrigin |
1,000,000,000 | 0.0092895362 DOT |
BuyExecution |
1,000,000,000 | 0.0092895362 DOT |
DepositAsset |
1,000,000,000 | 0.0092895362 DOT |
TOTAL | 4,000,000,000 | 0.0371581448 DOT |
Kusama¶
The total weight costs on Kusama take into consideration database reads and writes in addition to the weight required for a given instruction. Database read and write operations have not been benchmarked, while instruction weights have been. The breakdown of weight costs for the database operations is as follows:
Database | Read | Write |
---|---|---|
RocksDB (default) | 25,000,000 | 100,000,000 |
ParityDB | 8,000,000 | 50,000,000 |
Now that you are aware of the weight costs for database reads and writes on Kusama, you can calculate the weight cost for a given instruction using the base weight for an instruction.
For example, the WithdrawAsset
instruction has a base weight of 20,385,000
, and performs one database read, and one database write. Therefore, the total weight cost of the WithdrawAsset
instruction is calculated as:
20385000 + 25000000 + 100000000 = 145385000
The BuyExecution
instruction has a base weight of 3,697,000
and doesn't include any database reads or writes. Therefore, the total weight cost of the BuyExecution
instruction is 3,697,000
.
On Kusama, the benchmarked base weights are broken up into two categories: fungible and generic. Fungible weights are for XCM instructions that involve moving assets, and generic weights are for everything else. You can view the current weights for fungible assets and generic assets directly in the Kusama Runtime code.
With the instruction weight cost established, you can calculate the cost of the instruction in KSM.
In Kusama, the ExtrinsicBaseWeight
is set to 108,512,000
which is mapped to 1/10th of a cent. Where 1 cent is 10^12 / 30,000
.
Therefore, to calculate the cost of executing an XCM instruction, you can use the following formula:
XCM-KSM-Cost = XCMInstrWeight * KSMWeightToFeeCoefficient
Where KSMWeightToFeeCoefficient
is a constant (map to 1 cent), and can be calculated as:
KSMWeightToFeeCoefficient = 10^12 / ( 10 * 3000 * KSMExtrinsicBaseWeight )
Using the actual values:
KSMWeightToFeeCoefficient = 10^12 / ( 10 * 3000 * 108512000 )
As a result, KSMWeightToFeeCoefficient
is equal to 0.307185687604 Planck-KSM
. Now, you can begin to calculate the final fee in KSM, using KSMWeightToFeeCoefficient
as a constant and TotalWeight
(145,385,000) as the variable:
XCM-Planck-KSM-Cost = TotalWeight * KSMWeightToFeeCoefficient
XCM-KSM-Cost = XCM-Planck-KSM-Cost / KSMDecimalConversion
Therefore, the actual calculation for the WithdrawAsset
instruction is:
XCM-Planck-KSM-Cost = 145385000 * 0.307185687604
XCM-KSM-Cost = 44660191.1923 / 10^12
The total cost for that particular instruction is 0.000044660191 KSM
.
As an example, you can calculate the total cost of KSM for sending an XCM message that transfers xcKSM to KSM on Kusama using the following weights and instruction costs:
Instruction | Weight | Cost |
---|---|---|
WithdrawAsset |
145,385,000 | 0.000044660191 KSM |
ClearOrigin |
3,661,000 | 0.000001124607 KSM |
BuyExecution |
3,697,000 | 0.000001135665 KSM |
DepositAsset |
146,763,000 | 0.000045083493 KSM |
TOTAL | 299,506,000 | 0.000092003956 KSM |
Moonbeam-based Networks XCM Fee Calculation¶
Substrate has introduced a weight system that determines how heavy or, in other words, how expensive an extrinsic is from a computational cost perspective. One unit of weight is defined as one picosecond of execution time. When it comes to paying fees, users will pay a transaction fee based on the weight of the call that is being made, and each parachain can decide how to convert from weight to fee, for example, accounting for additional costs for transaction size, and storage costs.
For all Moonbeam-based networks, the generic XCM instructions are benchmarked, while the fungible XCM instructions still use a fixed amount of weight per instruction. Consequently, the total weight cost of the benchmarked XCM instructions considers the number of database reads/writes in addition to the weight required for a given instruction. The breakdown of weight cost for the database operations is as follows:
Database | Read | Write |
---|---|---|
RocksDB (default) | 25,000,000 | 100,000,000 |
Now that you know the weight costs for database reads and writes for Moonbase Alpha, you can calculate the weight cost for both fungible and generic XCM instruction using the base weight for instruction and the extra database read/writes if applicable.
For example, the WithdrawAsset
instruction is part of the fungible XCM instructions set. Therefore, it is not benchmarked, and the total weight cost of the WithdrawAsset
instruction is 200,000,000
, except for when transferring local XC-20s. The total weight cost for the WithdrawAsset
instruction for local XC-20s is based on a conversion of Ethereum gas to Substrate weight.
The BuyExecution
instruction has a base weight of 181,080,000
, and performs four database reads (assetManager
pallet to get the unitsPerSecond
). Therefore, the total weight cost of the BuyExecution
instruction is calculated as follows:
181080000 + 4 * 25000000 = 281080000
You can find all the weight values for all the XCM instructions in the following table, which apply to all Moonbeam-based networks:
Benchmarked Instructions | Non-Benchmarked Instructions |
---|---|
Generic XCM Instructions | Fungible XCM Instructions |
The following sections will break down how to calculate XCM fees for Moonbeam-based networks. There are two main scenarios:
- Fees paid in the reserve token (native tokens like GLMR, MOVR, or DEV)
- Fees paid in external assets (XC-20s)
Fee Calculation for Reserve Assets¶
For each XCM instruction, the weight units are converted to balance units as part of the fee calculation. The amount of Wei per weight unit for each of the Moonbeam-based networks is as follows:
Moonbeam | Moonriver | Moonbase Alpha |
---|---|---|
5,000,000 | 50,000 | 50,000 |
This means that on Moonbeam, for example, the formula to calculate the cost of one XCM instruction in the reserve asset is as follows:
XCM-Wei-Cost = XCMInstrWeight * WeiPerWeight
XCM-GLMR-Cost = XCM-Wei-Cost / 10^18
Therefore, the actual calculation for fungible instructions, for example, is:
XCM-Wei-Cost = 200000000 * 5000000
XCM-GLMR-Cost = 1000000000000000 / 10^18
The total cost is 0.001 GLMR
for an XCM instruction on Moonbeam.
Fee Calculation for External Assets¶
Considering the scenario with Alice sending DOT to Alith's account on Moonbeam, the fees are taken from the amount of xcDOT Alith receives. To determine how much to charge, Moonbeam uses a concept called UnitsPerSecond
, which refers to the units of tokens that the network charges per second of XCM execution time (considering decimals). This concept is used by Moonbeam (and maybe other parachains) to determine how much to charge for XCM execution using a different asset than its reserve.
Moreover, XCM execution on Moonbeam can be paid by multiple assets (XC-20s) that originate in the chain where the asset is coming from. For example, at the time of writing, an XCM message sent from Statemine can be paid in xcKSM, xcRMRK or xcUSDT. As long as that asset has an UnitsPerSecond
set in Moonbeam/Moonriver, it can be used to pay XCM execution for an XCM message coming from that specific chain.
To find out the UnitsPerSecond
for a given asset, you can query assetManager.assetTypeUnitsPerSecond
and pass in the multilocation of the asset in question.
If you're unsure of the multilocation, you can retrieve it using the assetManager.assetIdType
query.
For example, you can navigate to the Polkadot.js Apps page for Moonbeam and under the Developer dropdown, choose Chain State. From there, you can take the following steps:
- For the selected state query dropdown, choose assetManager
- Select the assetIdType extrinsic
- Under Option enter in the asset ID or toggle the include option off to return information for all of the assets. This example will get information for xcUNIT which has an asset ID of
42259045809535163221576417993425387648
- Click the + button to submit the query
You can take the result of the query and then use it to query the assetTypeUnitsPerSecond extrinsic:
- Make sure assetManager is selected
- Select the assetTypeUnitsPerSecond extrinsic
- For MoonbeamRuntimeXcmConfigAssetType, choose Xcm
- Enter
1
for parents - Select
Here
for interior - Click the + button to submit the query
The UnitsPerSecond
for xcDOT is 33,068,783,068
.
Remember that one unit of weight is defined as one picosecond of execution time. Therefore, the formula to determine execution time is as follows:
ExecutionTime = Weight / Picosecond
To determine the total weight for Alice's transfer of DOT to Moonbeam, you'll need the weight for each of the four XCM instructions required for the transfer:
Instruction | Weight |
---|---|
ReserveAssetDeposited |
200,000,000 |
ClearOrigin |
5,194,000 |
BuyExecution |
281,080,000 |
DepositAsset |
200,000,000 |
TOTAL | 686,274,000 |
Note
For the BuyExecution
instruction, the units of weight for the four database reads is accounted for in the above table.
With the total weight, you can calculate the execution time for Alice's transfer of DOT to Moonbeam using the following calculation:
ExecutionTime = 686274000 / 10^12
Which means that the four XCM instructions for the transfer cost 0.000686274
seconds of block execution time.
To calculate the total cost in xcDOT, you'll also need the number of decimals the asset in question uses, which for xcDOT is 10 decimals. You can determine the number of decimals for any asset by querying the asset metadata.
The block execution formula can then be used to determine how much Alice's transfer of DOT to Alith's account on Moonbeam costs. The formula for finding the total cost is as follows:
XCM-Cost = ( UnitsPerSecond / DecimalConversion ) * ExecutionTime
Then the calculation for the transfer is:
XCM-Cost = ( 33068783068 / 10^10 ) * 0.000686274
The total cost to transfer Alice's DOT to Alith's account for xcDOT is 0.0022694246 xcDOT
.
XCM Transactor Fees¶
The XCM Transactor Pallet builds an XCM message to remotely transact in other chains of the ecosystem. Developers can remotely transact through the pallet using the transactThroughSigned
extrinsic, in which the dispatcher account in the destination chain is a multilocation-derived account and must have enough funds to cover XCM execution fees plus whatever call is being remotely executed
Generally speaking, the XCM instructions normally involved for remote execution are:
- The first instruction handles tokens in the origin chain. This can be either moving tokens to a sovereign account or burning the corresponding XC-20 so that it can be used in the target chain. These instructions get executed in the origin chain
DescendOrigin
- mutates the origin with the multilocation provided in the instruction as the origin is no longer the sovereign account, but the multilocation-derivative accountWithdrawAsset
- gets executed in the target chain. Removes assets and places them into the holding registerBuyExecution
- gets executed in the target chain. Takes the assets from holding to pay for execution fees. The fees to pay are determined by the target chainTransact
- gets executed in the target chain. Dispatches the encoded call data from a given origin
This section covers how XCM fees are estimated for remotely transacting through a signed method.
Transact Through Signed Fees¶
When transacting through the multilocation-derivative account, the transaction fees are paid by the same account from which the call is dispatched, which is a multilocation-derived account in the destination chain. Consequently, multilocation-derived account must hold the necessary funds to pay for the entire execution. Note that the destination token, in which fees are paid, does not need to be register as an XC-20 in the origin chain.
Consider the following scenario: Alice wants to remotely transact in another chain (Parachain ID 888, in the Moonbase Alpha relay chain ecosystem) from Moonbase Alpha using the transact through signed extrinsic. To estimate the amount of tokens Alice's multilocation-derivative account will need to have to execute the remote call, you need to check the transact information specific to the destination chain. To do so, head to the chain state page of Polkadot.js Apps and set the following options:
- Choose the xcmTransactor pallet
- Choose the transactInfoWithWeightLimit method
- Set the multilocation for the destination chain from which you want to query the transact information. For this example, you can set parents to
1
- Select
X1
for interior - Select
Parachain
in the X1 field - Set Parachain to
888
- Click on +
From the response, you can see that the transactExtraWeightSigned
is 400,000,000
. This is the weight needed to execute the four XCM instructions for this remote call in that specific destination chain. Next, you need to find how much the destination chain charges per weight of XCM execution. Normally, you would look into the UnitsPerSecond
for that particular chain. But in this scenario, no XC-20 tokens are burned. Therefore, UnitsPerSecond
can be use for reference, but do not ensure that the amount of tokens estimated are correct. To get the UnitsPerSecond
as a reference value, in the same Polkadot.js Apps page, set the following options:
- Choose the xcmTransactor pallet
- Choose the destinationAssetFeePerSecond method
- Set the multilocation for the destination chain from which you want to query the transact information. For this example, you can set parents to
1
- Select
X2
for interior - Select
Parachain
in the X1 field - Set Parachain to
888
- Select
PalletInstance
in the X2 field - Set PalletInstance to
3
- Click on +
Note that this UnitsPerSecond
is related to the cost estimated in the Relay Chain XCM Fee Calculation section, or to the one shown in the Units per weight section if the target is another parachain. You'll need to find the correct value to ensure that the amount of tokens the multilocation-derivative account holds is correct. As before, calculating the associated XCM execution fee is as simple as multiplying the transactExtraWeight
times the UnitsPerSecond
(for an estimation):
XCM-Wei-Token-Cost = transactExtraWeight * UnitsPerSecond
XCM-Token-Cost = XCM-Wei-Token-Cost / DecimalConversion
Therefore, the actual calculation for one XCM Transactor transact through derivative call is:
XCM-Wei-Token-Cost = 400000000 * 50000000000000000
XCM-Token-Cost = 20000000000000 / 10^18
The cost for transacting through signed is 0.00002 TOKEN
. Note that this does not include the cost of the call being remotely executed, only XCM execution fees.
| Created: July 25, 2022