XCM Dialects: plugging into XCM to create standardized and predictable API surfaces

A common problem which XCM users have is the need for their chain to be able to construct and send calls which the receiving system can interpret when using Xcm::Transact. When interacting with a single chain, this is not a large issue, but becomes one when interfacing with many chains.

The desired goal is to have a predictable and common API surface for remote systems with respect to:

  1. Which operations they support
  2. How to construct Xcm::Transact packets that can achieve those operations.

To that end, previous suggestions have been to:

  1. Add more specific messages to XCM for use-cases like swapping, LP provisioning
  2. Adding more pallet-specific tooling to XCM for targeting specific pallets on a remote chain, without being aware of their particular index.

In fact, we can do better by changing our mindset around the Xcm::Transact message with a concept we refer to as ‘Dialects’ (thanks for the name, @karel).

The rough idea is for chains to change the way they configure the XCM pallet differently by providing a RuntimeCall type which is a wrapper around the raw call and the supported dialects.

In effect, this’d look something like this, plus versioning logic:

enum SupportedInboundXcmTransactions {
    Raw(Call), // direct `Call` to be executed on-chain
    DialectX(DialectXTransaction),
    DialectY(DialectYTransaction),
}

@shawntabrizi had some thoughts on a more specific format for this data structure, with versioning.

The receiving chain is free to determine how to handle DialectX and DialectY however it likes, mapping onto specific pallets in a way that can change between runtime upgrades, while sending chains get a predictable API interface.

We should also discuss discoverability of dialects from across chains - possibly with a similar ‘pub/sub’ system as is used to negotiate XCM versions themselves across chains in XCMv3.

This opens some space for community standards to emerge around the best way of doing common activities. For instance, the community could create a “DeFi” dialect that includes swaps, LP provisioning, or other common DeFi requirements. EVM calls, Oracles, Vaults are all also possibilities.

rel: Explore the idea of XCM dialects · Issue #6013 · paritytech/polkadot · GitHub

Thanks to Shawn, Brainjar, Karel, Keith for the initial discussion leading to this forum post.

4 Likes

I think the idea is good, but I have trouble to see how the dialect will look like, do you have an example of a common dialect (if possible not around transferring token) so I can better understand it.

I also suggested another idea ( [Discussion/Idea] Domain/Pallet specific XCM language · Issue #30 · paritytech/xcm-format · GitHub ) which is more oriented around pallet (even if XCM should not be related to pallets) as a temporary solution, but I’m open to the idea of dialect.

From the beginning I always had envisioned that XCM can also be used to create generic interfaces to a chain. This would for example be useful for ledger as they would only need to implement this generic interface. By generic interface I mean something like a staking interface. So, ledger would only need to implement a way to construct an extrinsic that sends a XCM using this staking interface and this could then be supported by almost any chain. Ledger would not need to adapt to every runtime upgrade, because this interface would be fixed.

What I call here interface is probably something similar to XCM dialects. While this is also not really a “real” xcm use-case, it would simplify quite a lot when interacting with different chains from a hardware signer.

3 Likes

I have a few ideas for dialects:

  • DeFi primitives (swap, borrow/lend)
  • EVM contracts (call, query storage)
  • Wasm contracts
  • Proxy Accounts
  • Governance operations

but realistically it’d be great to have input from the parachain teams which know their use-cases best, and who know the common use-cases which are shared with other teams best. Ultimately, the development of dialects needs to be a community endeavor.

With Uniques V2 and NFTs being introduced in XCMv3, dialects for NFT operations could be pretty useful, trading could happen more easily without moving the NFT between chains, and connecting with DeFi dialects the tokens used for the trade can be swapped immediately too. Furthermore, these dialects could also allow for the management of multi resourced NFTs that exist distributed across chains.

@gabe I’d be quite interested to see a proposal for an NFT-focused XCM dialect.

1 Like

@rphmeier

I think those dialects are good, but I’m worried they are too generic to be effective to enough parachains.
Let me try to bring some exemples.

====
DeFi primitives (swap, borrow/lend) is probably the easiest to imagine and implement across chains. Swapping is fairly common and easy to understand. I think this could be a dialect and would work kind of ok.

But how would it work for parachains which don’t have a unique swap/borrow/lend process. For example in Moonbeam, those are done in EVM DEXes, not by Moonbeam itself, so there are many ways to swap.
(Additionally, some parachains have swap mechanisms with additional properties, such as to limit impact on price fluctuations and …)

Would this mean that the dialect needs to support additional parameters for each of those cases, or simply that Moonbeam should not implement it ?

====
EVM contracts (call, query storage) has the benefit of being well defined (the EVM has been designed long time ago), however parachains don’t call it the same way. Some parachains are actually using substrate account (32 bytes usually) to access the EVM (the account gets converted) while some others use directly 20 bytes accounts.

Additionally, Moonbeam will not allow to call the EVM from a given account through XCM (for security reasons), meaning that we wouldn’t be able to fulfill something like evm.call(from: <my_account>, to:....., data:...)

====

Those are simple exemples showing there are many practical limitation preventing a common language/dialect between parachain at the moment I feel/fear.

What is your opinion?

Perhaps we should arrive at a decent amount of generalization while still maintaining the simple API. That could be done by adding optional (but named) parameters to the call, like those you mentioned (impact limit, price fluctuation), and accounts could be simply converted in the runtime.

Regarding the many different swap options available, like in a smart contract chain for example, the decision of which smart contract to relay the swap operation to could be handled on the runtime, perhaps by choosing the one with the best final performance (price, fees, etc).

The idea here is to reach a simple API that has just enough generalized that different chains have no issue implementing, but we should also understand that some of this work has to be handled in the specific chain runtimes.

But how would it work for parachains which don’t have a unique swap/borrow/lend process. For example in Moonbeam, those are done in EVM DEXes, not by Moonbeam itself, so there are many ways to swap.

I think this would have to go into the design of the dialect, e.g. by allowing chains to host multiple DEXes and addressing them either by an index or a MultiLocation. EVM chains could handle any internal invocation of various smart contracts.

Any dialect or interface risks being incomplete. These are valid design concerns that I would expect to see in a proposal.

Could you elaborate on why Moonbeam won’t allow XCM calls to trigger EVM execution? That is non-intuitive and quite surprising. It should at a minimum be allowed for the XCM to trigger execution on behalf of any sovereign account controlled by the origin.

You seem optimistic, so I’ll follow you on that idea of dialect :slight_smile:

Concerning preventing execution from XCM to EVM specifying the account id, the main reason is security. We don’t want in the case of a compromised/bad/bugged parachain, to allow access to any account on Moonbeam by simply passing any account in the multilocation.

Of course, the parachain can use its multilocation to access its sovereign accounts.
For users (account multilocation) we hash the multilocation into an account. This gives a unique account per multilocation.

Ex: /parachains/2021/accounts/0x123.....4923 => EVM account 0xab23....4abff
The user can still deposit tokens to whatever address they want on Moonbeam from this “hashed” address (which contains the tokens retrieved through XCM). We also support setting proxies from your real address to your hashed address to allow direct execution, but this comes with the risk of allowing any parachain to act on your account.

1 Like

Thanks for clarifying. So it is the case that parachains or other remote origins are allowed to trigger EVM calls by their own sovereign accounts, but parachains aren’t allowed to execute on behalf of arbitrary accounts. This seems reasonable and matches my intuition about how XCM would be used in the EVM scenario; users should be required to explicitly delegate permission to a parachain via a smart contract.

In that case, I think an EVM dialect should still be viable.

I think this can be broke down into two cases:

  1. When a user want to swap/borrow/lend, and expect the best result possible. This can be implemented by an aggregation pallet, for instance a DEX aggregator pallet which:
    • Define a standard precompile interface to query liquidity and do swap.
    • Allow contracts which implement this interface to register and be aggregated.
    • The aggregator compares at runtime and execute the best swap.
  2. When a user want to do via a specific contract. This probably can be done by EVM contract dialect. Another option is to design dialect instructions additional to default Swap, like SwapVia with contract address param.