# XCM fee estimation It's long been a problem to estimate execution fees for XCM. This became even more of a problem once delivery fees were introduced. In order to fix this issue, two new runtime APIs were introduced to existing runtimes and are simple to integrate into any runtime that wants this functionality. - [XcmPaymentApi](https://paritytech.github.io/polkadot-sdk/master/xcm_runtime_apis/fees/trait.XcmPaymentApi.html), originally introduced in [PolkadotSDK#3607](https://github.com/paritytech/polkadot-sdk/pull/3607). - [DryRunApi](https://github.com/paritytech/polkadot-sdk/pull/3872), originally introduced in [PolkadotSDK#3872](https://github.com/paritytech/polkadot-sdk/pull/3872). The `XcmPaymentApi` allows users to query the execution and delivery fees required by XCMs. ## Using the APIs Let's first talk about using these APIs to solve the problem of estimating XCM fees. To do this, we'll estimate fees for an example. Alice is using AssetHub but has to create an identity on the People chain. To do this, she has to transfer some DOT to the People chain via XCM. ```mermaid graph LR A(Alice) --"execute(xcm)"--> AH(AssetHub) AH --"send(xcm)"--> People(People) ``` What fees are there in this example? There are: - local execution fees in AssetHub - delivery fees taking the message from AssetHub to People - remote execution fees in People ```mermaid graph LR AH(AssetHub) --"local execution fees"--> AH AH --"delivery fees"--> People(People) People --"remote execution fees"--> People ``` The overall fees are `local_execution_fees + delivery_fees + remote_execution_fees`. Let's take a look at how we can calculate these. We'll use [PolkadotAPI (PAPI)](https://papi.how/) for these snippets. ### The XCM we're sending We'll be using V4 for sending a Teleport. ```typescript const xcm = { type: 'V4', value: [ { type: 'WithdrawAsset', value: [ { id: { parents: 1, value: { type: 'Here', value: undefined } }, fun: { type: 'Fungible', value: 100n } } ], }, { type: 'BuyExecution', value: { asset: { id: { parents: 1, interior: { type: 'Here', value: undefined } }, fun: { type: 'Fungible', value: 100n }, }, }, }, { type: 'InitiateTeleport', value: { destination: ..., assets: [...], remote_fees: { id: { parents: 1, interior: { type: 'Here', value: undefined } }, fun: { type: 'Fungible', value: 1n }, }, remote_xcm: [ { type: 'DepositAsset', value: ... }, ], } }, ], }; ``` The process is first putting a large value in `PayFees`, then estimate the fees and then put them back in. ### Local execution fees For getting the `local_execution_fees` on `AssetHub`, `Alice` can use the `XcmPaymentApi`. She first weighs the XCM using `query_xcm_weight`, this returns a weight value. This weight can be turned into a fee, this can be done with `query_weight_to_asset_fee`, but an asset also has to be specified. To get a list of all assets that can be used for fee payment, `query_acceptable_payment_assets` can be used. Since Alice wants to use DOT, she can specify `{ parents: 1, interior: Here }`, the location of the Polkadot network (relay chain) relative to AssetHub. Here's what it might look like with PAPI (Typescript): ```typescript // These will be set if the runtime API calls are successful. let localExecutionFees = 0; // We query the weight of the xcm. const weightResult = await api.apis.XcmPaymentApi.query_xcm_weight(xcm); if (weightResult.success) { // We convert the weight to a fee amount. // The asset is { parents: 1, interior: Here }, aka, DOT. const executionFeesResult = await api.apis.XcmPaymentApi.query_weight_to_asset_fee( weightResult.value, XcmVersionedAssetId.V4({ parents: 1, interior: { type: 'Here', value: undefined }, }), ); if (executionFeesResult.success) { localExecutionFees = executionFeesResult.value; } } ``` ### Delivery fees To get all other fees, we need to take a look at the `DryRunApi`. This API lets you dry-run any call or xcm and receive a bunch of information about it. Since Alice uses pallet-xcm's `execute` call to submit her xcm, she has to dry-run the xcm. This can be done with the `dry_run_xcm` function of the API. This function takes the xcm, but also the origin with which we are executing it. These means this API can be used with any origin you want, useful for dry-running [governance proposals](https://forum.polkadot.network/t/a-website-to-dry-run-calls-with-any-origin/10786). Since Alice is an account on AssetHub, her location relative to AssetHub (and thus her origin) is `{ parents: 0, interior: X1(AccountId32 { id: Alice, network: Polkadot }) }`. The result of this dry-run will be a collection of things: - `execution_result`: Whether or not the actual execution would succeed. - `emitted_events`: All events emitted during execution. - `forwarded_xcms`: A list of xcms sent to other locations. More about this return type can be seen in the [Rust docs](https://paritytech.github.io/polkadot-sdk/master/xcm_runtime_apis/dry_run/struct.XcmDryRunEffects.html). We can now use these `forwarded_xcms` to know what message was sent where and plug it in `XcmPaymentApi::query_delivery_fees`. This function takes the destination and the message. Here's how it might look like with PAPI: ```typescript let deliveryFees = 0n; const origin = VersionedLocation.V4({ parents: 1, interior: { type: 'Here', value: undefined }, }); // We dry run the local xcm. const dryRunResult = await api.apis.DryRunApi.dry_run_xcm(origin, xcm); if (dryRunResult.success) { // We extract the sent xcms from the dry run result. const { forwarded_xcms: forwardedXcms } = dryRunResult.value; const xcmsToPeople = forwardedXcms.find(([location, _]) => ( location.type === 'V4' && location.value.parents === 1 && location.value.interior.type === 'X1' && location.value.interior.value[0].type === 'Parachain' && location.value.interior.value[0].value === 1004 // People's ParaID. )); const [destination, messages] = xcmsToPeople[0]; const remoteXcm = messages[0]; const deliveryFeesResult = await api.apis.XcmPaymentApi.query_delivery_fees(destination, remoteXcm); if (deliveryFeesResult.success) { const assets = deliveryFeesResult.value; deliveryFees = ( assets.type === 'V4' && assets.value[0].fun.type === 'Fungible' && assets.value[0].fun.value.valueOf() ) || 0n; } } ``` ### Remote execution fees For calculating the remote execution fees we'll reuse the result of the dry-run we got from the last section. Since we have the destination and the xcm that will be executed there, we can identify the destination chain and create a client connecting to it. Then, we can do the same thing we did in the "Local execution fees" section but on the destination chain. If there are more hops in the journey, this process can be repeated infinitely. ### The final XCM ```typescript ``` ## Integrating the APIs to a runtime Now for the runtime section of this guide. These APIs are already implemented in system parachains, but how do I implement it in my parachain? This is thankfully very easy thanks to most of the implementation being abstracted away in `pallet-xcm`. ### XcmPaymentApi Here's a snippet for implementing this API on a runtime: ```rust impl xcm_runtime_apis::fees::XcmPaymentApi<Block> for Runtime { fn query_acceptable_payment_assets(xcm_version: xcm::Version) -> Result<Vec<VersionedAssetId>, XcmPaymentApiError> { let acceptable_assets = vec![AssetId(NativeToken::get())]; XcmPallet::query_acceptable_payment_assets(xcm_version, acceptable_assets) } fn query_weight_to_asset_fee(weight: Weight, asset: VersionedAssetId) -> Result<u128, XcmPaymentApiError> { let latest_asset_id: Result<AssetId, ()> = asset.try_into(); match latest_asset_id { Ok(asset_id) if asset_id.0 == NativeToken::get() => { Ok(WeightToFee::weight_to_fee(&weight)) }, _ => todo!("Error handling"), } } fn query_xcm_weight(message: VersionedXcm<()>) -> Result<Weight, XcmPaymentApiError> { XcmPallet::query_xcm_weight(message) } fn query_delivery_fees(destination: VersionedLocation, message: VersionedXcm<()>) -> Result<VersionedAssets, XcmPaymentApiError> { XcmPallet::query_delivery_fees(destination, message) } } ``` As you can see, the most work you need to do is in `query_acceptable_payment_assets` and `query_weight_to_asset_fee` since they depend on how many assets your runtime accepts for fee payment. In the AssetHub runtime, for example, both DOT and any asset in a liquidity pool with DOT are accepted for fee payment. The implementations of these functions are appropriately more complicated. ### DryRunApi This API is even more straightforward to implement. The most important part is that you correctly specify the generics that pallet-xcm requires. Here's a snippet for implementing this API on a runtime: ```rust impl xcm_runtime_apis::dry_run::DryRunApi<Block, RuntimeCall, RuntimeEvent, OriginCaller> for Runtime { fn dry_run_call(origin: OriginCaller, call: RuntimeCall) -> Result<CallDryRunEffects<RuntimeEvent>, XcmDryRunApiError> { XcmPallet::dry_run_call::<Runtime, xcm_config::XcmRouter, OriginCaller, RuntimeCall>(origin, call) } fn dry_run_xcm(origin_location: VersionedLocation, xcm: VersionedXcm<RuntimeCall>) -> Result<XcmDryRunEffects<RuntimeEvent>, XcmDryRunApiError> { XcmPallet::dry_run_xcm::<Runtime, xcm_config::XcmRouter, RuntimeCall, xcm_config::XcmConfig>(origin_location, xcm) } } ```