-
Notifications
You must be signed in to change notification settings - Fork 710
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
XCM Asset Transfer Program Builder #4736
Comments
This issue has been mentioned on Polkadot Forum. There might be relevant details there: https://forum.polkadot.network/t/rfc-xcm-asset-transfer-program-builder/8528/1 |
This issue has been mentioned on Polkadot Forum. There might be relevant details there: https://forum.polkadot.network/t/rfc-xcm-asset-transfer-program-builder/8528/2 |
I really like the API for this! |
This issue has been mentioned on Polkadot Forum. There might be relevant details there: https://forum.polkadot.network/t/rfc-xcm-asset-transfer-program-builder/8528/3 |
We could start simple: single transfer type per hop (for mixing multiple asset transfer types we need RFC 100, earliest: XCMv5). Simple example: USDC from Bifrost to Polkadex (through AH)fn example_bifrost_to_ah_to_polkadex() {
// Initial context needs to start with `GlobalConsensus`
let bifrost_context = InteriorLocation::X2(GlobalConsensus(Polkadot), Parachain(BIFROST_PARAID));
// used assets IDs as seen by the `bifrost_context` chain
let usdc_id = AssetId(Location::new(
1,
X3(Parachain(ASSET_HUB_PARAID), PalletInstance(50), GeneralIndex(1337)),
));
// Initialize the builder by defining the starting context and,
let asset_transfer_builder = AssetTransferBuilder::using_context(bifrost_context)
// registering easy-to-use tags for the assets to transfer - caller can use these tags to
// easily identify the assets without having to worry about reanchoring and contexts
.define_asset("USDC", usdc_id)
// create the builder
.create();
let xcm = asset_transfer_builder
// the starting context is Bifrost
// withdraw assets to transfer from origin account
.withdraw("USDC", Definite(usdc_amount))
// set AssetHub as the destination for this leg of the transfer
.set_next_hop(Location::new(1, X1(Parachain(ASSET_HUB_PARAID))))
// reserve-withdraw all USDC on Asset Hub
.transfer("USDC", All, ReserveWithdraw)
// use USDC to pay for fees on Asset Hub (can define upper limit)
.pay_remote_fees_with("USDC", Definite(max_usdc_to_use_for_fees))
// "execute" current leg of the transfer, move to next hop (Asset Hub)
.execute_hop()
// from here on, context is Asset Hub
// set Polkadex as the destination for this (final) leg of the transfer
.set_next_hop(Location::new(1, X1(Parachain(POLKADEX_PARAID))))
// reserve-deposit USDC to Polkadex (asset reanchoring done behind the scenes)
.transfer("USDC", All, ReserveDeposit)
// use USDC to pay for fees on Polkadex (no limit)
.pay_remote_fees_with("USDC", All)
// "execute" current leg of the transfer, move to next hop (Polkadex)
.execute_hop()
// from here on, context is Polkadex
// deposit all received assets to `beneficiary`
.deposit_all(beneficiary)
// build the asset transfer XCM Program!
.finalize();
// Profit!
println!("Asset transfer XCM: {:?}", xcm);
} This would create the following XCM program:
This XCM can be executed on Bifrost with |
Bridge example - KSM from Kusama Treasury to Interlay (over bridge, going through both Asset Hubs)Showcasing how any Polkadot parachain can get funding from Kusama Treasury (same works for Kusama parachains and Polkadot Treasury): fn KSM_from_Kusama_to_Interlay() {
// Initial context needs to start with `GlobalConsensus`
let kusama_context = InteriorLocation::X1(GlobalConsensus(Kusama));
// used assets IDs as seen by the `kusama_context` chain
let ksm_id = AssetId(Location::new(0, Here));
// Initialize the builder by defining the starting context and,
let asset_transfer_builder = AssetTransferBuilder::using_context(kusama_context)
// registering easy-to-use tags for the assets to transfer - caller can use these tags to
// easily identify the assets without having to worry about reanchoring and contexts
.define_asset("KSM", ksm_id)
// create the builder
.create();
let xcm = asset_transfer_builder
// the starting context is Kusama
// withdraw assets to transfer from origin account
.withdraw("KSM", Definite(ksm_amount))
// set Kusama AssetHub as the destination for this leg of the transfer
.set_next_hop(Location::new(1, X1(Parachain(KUSAMA_ASSET_HUB_PARAID))))
// teleport all KSM on Asset Hub
.transfer("KSM", All, Teleport)
// use KSM to pay for fees on Kusama Asset Hub (no limit)
.pay_remote_fees_with("KSM", All)
// "execute" current leg of the transfer, move to next hop (Kusama Asset Hub)
.execute_hop()
// from here on, context is Kusama Asset Hub
// set Polkadot Asset Hub as the destination for this leg of the transfer
.set_next_hop(Location::new(
2,
X2(GlobalConsensus(Polkadot), Parachain(POLKADOT_ASSET_HUB_PARAID)),
))
// reserve-deposit KSM to Polkadot Asset Hub (asset reanchoring done behind the scenes)
.transfer("KSM", All, ReserveDeposit)
// use KSM to pay for fees on Polkadot Asset Hub (no limit)
.pay_remote_fees_with("KSM", All)
// "execute" current leg of the transfer, move to next hop (Polkadot Asset Hub)
.execute_hop()
// from here on, context is Polkadot Asset Hub
// set Interlay as the destination for this leg of the transfer
.set_next_hop(Location::new(1, X1(Parachain(INTERLAY_PARAID))))
// reserve-deposit KSM to Interlay (asset reanchoring done behind the scenes)
.transfer("KSM", All, ReserveDeposit)
// use KSM to pay for fees on Interlay (no limit)
.pay_remote_fees_with("KSM", All)
// "execute" current leg of the transfer, move to next hop (Interlay)
.execute_hop()
// from here on, context is Interlay
// deposit all received assets to `beneficiary`
.deposit_all(beneficiary)
// build the asset transfer XCM Program!
.finalize();
// Profit!
println!("Asset transfer XCM: {:?}", xcm);
} Would create the following XCM program:
|
This issue has been mentioned on Polkadot Forum. There might be relevant details there: https://forum.polkadot.network/t/polkadot-technical-summit-xcm/9384/1 |
Following the enablement of arbitrary XCM execution on Kusama and Polkadot system chains, users/wallets/dapps can interact uniformly across multiple chains directly using XCM programs, without having to figure out chain-specific pallets or calls/extrinsics.
Improving Asset Transfer UX/DX
Cross-chain asset transfers are currently hard to generalize across the ecosystem. Each Parachain uses its own assets transfer pallet (usually
xtokens
orpallet-xcm
) that exposes asset transfer extrinsics unique to that chain, and wallets/dapps need to integrate with all of them. These extrinsics then build opinionated XCM programs to do the asset transfers.With the ability to execute arbitrary XCM programs, we can make use of this powerful layer of abstraction, and slowly transition to switching that around:
Wallets/dApps define their desired asset transfers in an XCM program and execute that using the "universal"
pallet-xcm::execute()
extrinsic. This has two major advantages:xtokens
andpallet-xcm
transfer extrinsics are limited to basic use-cases, and trying to expose more flexible ones results in ugly calls like pallet-xcm::transfer_assets_using_type_and_then().The disadvantage of running "raw" XCM programs is that they're hard to build, it's a low-level language with gotchas and corner-cases, and easy to get wrong. Calling an extrinsic to build and execute an XCM program for you is definitely simpler and more reliable.
But what if we could fix (or mitigate) that?
XCM Asset Transfer Tooling
For automated, safe, reliable building of a complex asset transfer XCM we will require multiple layers of "helper" tools/libraries each operating at a different level. A quick exercise of imagination produces the following top-down view:
wallet/dApp: handles the "business usecase": gets the user input and models "actions" based on it: some of these actions will be generic asset transfers like:
AssetX
fromAccountA
onChainA
toAccountB
onChainB
".some asset-transfer-library (ecosystem-stateful): has ecosystem-level knowledge/state, able to identify what cross-chain interactions are required to "move
AssetX
fromChainA
toChainB
", depending on the actual assets and chains involved it can define things like:AssetX
,ChainA
toChainB
(is it direct, through a reserve chain, over a bridge, etc?), and the "hops" involvedlibraries like asset-transfer-api and its assets-registry (cc @IkerAlus)
some XCM builder tool (ecosystem-stateless): Easy to use helper tool/library to hide away as much XCM complexity and be able to take the "detailed definition" of an asset transfer (involved accounts, assets, chains, transfer-types) and build a sanity-checked, corner-case-proofed, reliable XCM program for it.
This ^, alongside tooling that exposes the DryRun runtime APIs, should allow dApps to confidently switch to using flexible, portable, interoperable XCM programs instead of opinionated, chain-specific asset transfer extrinsics.
XCM Asset Transfer Builder
This RFC proposes the following (Rust) builder pattern tool for addressing layer (3) above. Same or similar can be created in JS or other relevant languages.
Builder properties:
Location
s context-relative views (no need to worry about reanchoring all the time),Example 1 - HDX, USDT, GLMR from Hydration Network to Moonbeam (through AH)
Example 2 - KSM from Karura to Acala (over bridge, going through both Asset Hubs)
cc @xlc @franciscoaguirre
The text was updated successfully, but these errors were encountered: