---
# System prepended metadata

title: Transfer over bridge via pallet_xcm

---

Will use the scenario of sending KSM from Kusama's Asset Hub to Polkadot's as an example. Will work the same way in the opposite direction.

Abbreviations:

- AHP: Asset Hub Polkadot
- AHK: Asset Hub Kusama
- SO_OF_AH*: Sovereign account of Asset Hub {Polkadot, Kusama}
- ED: Existential Deposit

## fees

User pays: `transaction fee` + `transport fee`. Ultimately `transport fee` should cover XCM transport costs on the route (including bridge cost) - in following bridge iterations/improvements.

At bridge initial launch:
- BHs allow free XCM exec for AHs origins on same relay (BHK allows free exec for AHK) - will probably change in future iterations of the bridge as part of better aligning charged fees with actual chain(s) costs (e.g. replace the `UnpaidRemoteExporter` with the `SovereignPaidRemoteExporter` (at the sending parachain) to inject `BuyExecution` instruction that will buy execution at the bridge hub) - will be part of `transport_fee`
- HRMP transport fees (between AHs and BHs) are also currently **zero** - this might change (see https://github.com/paritytech-secops/srlabs_findings/issues/315) and be included in `transport_fee`
- Bridge fee is static fee to be included in `transport_fee` - future work will make it dynamic (and hopefully cheaper)
- AHP and AHK **do not** allow free XCM execution of each other - `BuyExecution` is required - this will also be part of `transport_fee`

### Case: Transfer KSM from AHK -> AHP

`transport_fee` = `bridge_fee` + `XCM exec cost on AHP` (we call this cost of executing XCM on other side: `swap_fee_portion`)

### Current `pallet_xcm::reserve_transfer_assets()` doesn't work over the bridge:

- When using `pallet_xcm::reserve_transfer_assets`, as explained in the docs, "Fee payment on the destination side is made from the asset in the `assets` vector of index `fee_asset_item`." However, the assets in the local context are not "sufficient" in the destination consensus system (e.g. KSM on AHP). `BuyExecution` using KSM on AHP will not work.

### Desired State (Pay fees on destination from SA of source UniversalLocation for `reserve_transfer_assets()`):

User pays `transaction fee` + `transport fee` in KSM (or other sufficient asset) withdrawn from the transferred sum. The fees go to AHK treasury/staking-pot. The _DOT_ required for `BuyExecution` on dest, will be withdrawn from the sovereign account of AHK on AHP on dest.

AssetHubKusama (`pallet_xcm`) would have new logic (see below "How to test"), which can take part of the KSM assets (the `transport_fee`) and add it to AHK treasury. This amount of KSM should cover the DOT withdrawn from SA_OF_AHK on AHP (i.e., AHK treasury gets KSM on source, spends DOT on target).

In practice, each chain's treasury will occasionally need to rebalance/top up the sovereign account of its partner Asset Hub.

In the initial bridge launch, `KSM/DOT` exchange rate will be fixed, once we have a DEX available we can use it for real exchange rate. 

For example, let's assume we configure a "treasury exchange rate" of 1KSM:4DOT to use in `swap_fee_portion`.

This is how an KSM transfer from AHK -> AHP looks like (with before and after):

**AssetHubKusama (works with PoC)**

| Account | Before | Transfer | After |
| -------- | -------- | -------- | -------- |
| alice        | ED_KSM + 100KSM + 0.05KSM | -100KSM (99KSM as reserve, 1KSM as `transport_fee = bridge_fee + swap_fee_portion`) -0.05KSM (`extrinsic fee`) | ED_KSM |
| fee handler  | ED_KSM  | +0.05KSM                     | ED_KSM  + 0.05KSM|
|   SA_OF_AHP  | ED_KSM  | ReserveAssetDeposited(99KSM) | ED_KSM + 99KSM   |
| AHK treasury | ED_KSM  | +1KSM                        | ED_KSM + 1KSM    |

**AssetHubPolkadot (works with PoC)**

| Account   | Before | Transfer | After |
| --------- | -------- | -------- | -------- |
| alice     | ED_DOT          | ReserveAssetDeposited(99KSM) | ED_DOT, 99wKSM   |
| SA_OF_AHK |  ED_DOT + 50DOT (prefunded) | `WithdrawAsset(4DOT), BuyExecution(4DOT), RefundSurplus, DepositAsset(All)` (real weight bought for 1.5DOT, 2.5DOT is refunded)| ED_DOT + 48,5DOT  |
| `Trader`  | ED_DOT          | +1.5DOT                      | ED_DOT  + 1.5KSM |

### PoC on AssetHubPolkadot

We want to `BuyExecution` on destination from SA_OF_AHK. But the problem/issue is that `XcmExecutor` adds `ClearOrigin` after `ReserveAssetDeposited` to be executed on AHP (destination). But we want to buy execution from SA_OF_AHK with instructions like this:
```
WithdrawAsset(4DOT),
BuyExecution(4DOT),
RefundSurplus,
DepositAsset(AllDOT)
```
unfortunately, they are appended after `ClearOrigin` [here](https://github.com/paritytech/polkadot/blob/master/xcm/xcm-executor/src/lib.rs#L521-L524),

which has two issues/implications:

1. We initially have the origin as AssetHubKusama `UniversalOrigin(GlobalConsensus(Kusama)), DescendOrigin(X1(Parachain(1000))),`, which is correct. But after `ClearOrigin` -> `self.context.origin = None` we lose origin, so the instructions: `WithdrawAsset/BuyExecution/RefundSurplus/DepositAsset` will fail.
2. These instructions at first don't pass the barrier [here](https://github.com/paritytech/polkadot/blob/master/xcm/xcm-builder/src/barriers.rs#L88-L89), because it expects `BuyExecution` after `ClearOrigin`, but we have`WithdrawAsset` there.

So, the PoC uses instruction `AliasOrigin` which is set with `SA_OF_AHK`, so the `WithdrawAsset/BuyExecution` work.

PoC can be seen here: https://github.com/paritytech/polkadot/pull/7456


### Open questions:

1. Is this approach with `Pay fees on destination from SA of source UniversalLocation` ok?
2. If so, is this implementation with `AliasOrigin` ok?
 - Another option would be to change executor to have a runtime-specific `T::FeeTransactor::append()` inject `WithdrawAsset, BuyExecution` in-between (or before)`ReserveAssetDeposited/ReserveAssetTeleported` and `ClearOrigin`.
3. Can we change the Barrier above? Should we create new Barrier? (PoC added new dedicated barrier)
4. New xcm instuction? (We hope that we would be able to do this with XCMv3)
5. `is_sufficient` for wrapped assets would fix issue and we dont need `pallet_xcm` customization

## **How to test:**

Relevant changes to `pallet_xcm`: https://github.com/paritytech/polkadot/pull/7456

Test-cases with AssetHubKusama -> AssetHubPolkadot: https://github.com/paritytech/cumulus/pull/2762

```
git clone https://github.com/paritytech/cumulus.git
git checkout -b bko-transfer-asset-via-bridge-pallet-xcm --track origin/bko-transfer-asset-via-bridge-pallet-xcm

#------------------------
# 1. KSM -> AssetHubPolkadot

# simulate pallet_xcm::reserve_transfer_assets on AssetHubKusama
# (prints xcm to log)
cargo test --package asset-hub-kusama-runtime --test tests transfer_asset_via_bridge_initiate_reserve_based_for_native_asset_works -- --exact

# simulate received message on AssetHubPolkadot
cargo test --package asset-hub-polkadot-runtime --test tests receive_reserve_asset_deposited_from_different_consensus_works -- --exact

#------------------------
# 2. DOT -> AssetHubKusama

# simulate pallet_xcm::reserve_transfer_assets on AssetHubPolkadot
# (prints xcm to log)
cargo test --package asset-hub-polkadot-runtime --test tests transfer_asset_via_bridge_initiate_reserve_based_for_native_asset_works -- --exact

# simulate received message on AssetHubKusama
cargo test --package asset-hub-kusama-runtime --test tests receive_reserve_asset_deposited_from_different_consensus_works -- --exact
```
