pallet-xcm: XCM assets reserve withdraw feature
Motivation
Historically pallet-xcm is basic Polkadot pallet for sending XCM, including assets manipulation functions like reserve_transfer_assets call. Unfortunately pallet-xcm miss function for getting reserved / transferred assets back.
For example, reserve_transfer_assets send multi-asset using TransferReserveAssets instruction, which process lock asset and notify destination parachain about.
https://github.com/paritytech/polkadot/blob/d1002106ab43f7b01cc86ad9aa2fe41d4b4a8f5f/xcm/pallet-xcm/src/lib.rs#L816
But in pair to it, I believe, should exist a way for get these assets back using InitiateReserveWithdraw instruction, which burn synthetic asset on chain and send notification to destination for releasing locked asset.
Solution
This PR implements reserve_withdraw_asset in pair to reserve_transfer_assets, it uses InitiateReserveWithdraw XCVM instruction and can be used for sending reserved assets back. It release ecosystem projects of using multiple XCM pallets like xtokens / pallet-xcm: one for assets deposing and other for assets withdraw.
Applying this PR makes possible ecosystem projects to use pallet-xcm only for full XCM assets support.
Adding an extrinsic to execute an XCM that includes InitiateReserveWithdraw doesn't simply allow the destination chain to send synthetic/derivative/bookkeeping assets back to the reserve, it also allows any two chains to send/receive said assets via an intermediary reserve chain.
We'll need to think thoroughly about the implications of such an extrinsic first to see whether it's a fit for the purposes of the XCM pallet.
Thank you for feedback @KiChjang, I believe if pallet-xcm includes reserve transfer (deposit) function then it should have reserve withdraw function.
Looking forward to your final decision.
I would just expose do_reserve_withdraw_assets as reserve_withdraw_assets, rather than having two separate extrinsics.
@gavofyork thank you for fixes.
I just made do_* and call separately in manner of reserve_transfer_assets/limited_reserve_transfer_assets, it makes possible to provide 'limited' version of same call.
This pull request has been mentioned on Polkadot Forum. There might be relevant details there:
https://forum.polkadot.network/t/asset-multilocation-punning/423/1
@KiChjang just to make summary of this PR high-level workflow discussion.
How reserve transfer should works:
- The Alice wan't to transfer asset(T) to Bob from parachain A to parachain B (T has MultiLocation = X);
- The Alice send extrinsic
reserve_transfer_assetswhich precisely send XCM in form
let xcm = Xcm(vec![
BuyExecution { fees, weight_limit },
DepositAsset { assets: Wild(All), max_assets, beneficiary },
]);
let mut message = Xcm(vec![TransferReserveAsset { assets, dest, xcm }]);
where parachain B receives message:
Xcm(vec![
ReserveAssetDeposited(assets),
ClearOrigin,
BuyExecution { fees, weight_limit: Limited(0) },
DepositAsset { assets: Wild(All), max_assets, beneficiary },
]);
- This step is important, because the parachain B dont receives precisely same multi-asset as parachain A sent. I believe that anchor/reanchor wrappers in XCM executor is used for making different multi-asset location before send:
https://github.com/paritytech/polkadot/blob/master/xcm/xcm-executor/src/lib.rs#L314
- Reverse way is the same but uses
InitiateReserveWithdrawopcode. XCM executor reanchor multi-location of asset before sending it back to parachain A.
let xcm = Xcm(vec![
BuyExecution { fees, weight_limit },
DepositAsset {
assets: Wild(All),
max_assets,
beneficiary,
},
]);
let message = Xcm(vec![
WithdrawAsset(assets),
InitiateReserveWithdraw {
assets: Wild(All),
reserve: dest,
xcm,
},
]);
XCM message don't directly use user sent multi-asset values, XCM executor converts it before sent:
https://github.com/paritytech/polkadot/blob/master/xcm/xcm-executor/src/lib.rs#L415
So, I guess your concern should be applied not for this PR because it uses XCM executor anchor/reanchor logic.
Why it looks like parachains uses same asset id for local and remote assets, I guess it is because of
AssetIdconversion used insideAssetTransactorthat should definitely have 1-1 mapping between numeric asset_id and asset multi-location.
@akru From your description, it would seem to me that it isn't clear how re-anchoring works to you. All re-anchoring does is rewrite the MultiLocation from the perspective of the destination, i.e. if your asset was originally written as ../Here, re-anchoring this to the relay chain would simply change it to Here, or if the destination is Statemint, it would still stay the same as ../Here.
If we have a derivative asset that has a MultiLocation of PalletIndex(17)/GeneralIndex(42) instead, then reanchoring this MultiLocation to the relay chain would yield Parachain(your_para_id)/PalletIndex(17)/GeneralIndex(42), and if the destination is Statemint, it would be re-anchored to ../Parachain(your_para_id)/PalletIndex(17)/GeneralIndex(42).
I hope you can see that the MultiLocation still points to the derivative asset, and not the reserve asset. In short, re-anchoring does not magically convert the MultiLocation of the derivative asset to that of the reserve asset.
@KiChjang thank you for explanation. I see, here you meant derivative asset in your opinion should have totally different multi-location without any reference to original, right? So, in this case we should have some kind of mapping between derivative and reserved asset, that makes things more and more complex, didn’t?