Skip to content

List of supported XCM assets Runtime API #4988

@mrshiposha

Description

@mrshiposha

The current version of the pallet-xcm makes it easy to transfer assets without thinking of what transfer type you need as the pallet computes that by itself (thanks to the transferAssets extrinsic). The ORML pallet-xtokens does a similar thing with its transferMultiassets extrinsic.

This simplifies the XCM interaction from the JS side drastically and allows the building of more convenient UIs.

However, at the moment, users cannot discover the complete list of supported XCM assets uniformly in the ecosystem chains.
Note: "Supported" means the asset can be withdrawn/transferred/deposited on that chain. It doesn't matter if the asset is supported as a fee asset.

This leads to the creation of custom XCM asset registries that must be constantly maintained.
Such lists of supported XCM assets are essential for building convenient UIs for the end-users.

If we had a uniform way of asking chains what they support, we'd have an opportunity to build general (and simple!) XCM JS tooling and much better UIs that are always up-to-date with chains' valid state.

Suggestion

Of course, we could develop a Runtime API that returns a simple list of supported assets.
This would've been enough for fungibles, as their asset location and amount fully identifies the balances.
For non-fungibles, however, we must also know the asset location's supported AssetInstance variant (Index, Array4, etc.).
So, the Runtime API should return the list of supported asset locations and also report their fungibility variant.

Native approach

We could add additional types for such Runtime API responses. However, I think this is neither a future-proof nor a convenient approach (the alternative is in the next section).
Nonetheless, I will outline these possible extra types:

enum SupportedFungibility {
    Fungible,
    NonFungible(VersionedSupportedNonFungible)
}

// Must also have a `Versioned*` type
enum SupportedNonFungible {
    Index,
    Array4,
    /*
      <...>
       the same variants as in the `AssetInstance` but without parameters
    */
}

// Must also have a `Versioned*` type
struct SupportedAsset {
    id: VersionedAssetId,
    fun: VersionedSupportedFungibility,
}

And the Runtime API could look like this:

trait SupportedXcmAssets<Metadata> {
    fn query_supported_assets(version: Version) -> Result<Vec<VersionedSupportedAsset>, Error>;

    // chain-specific info like `is_sufficient`. Useful for UIs
    fn query_asset_metadata(asset: VersionedAsset) -> Result<Metadata, Error>;
}

URI approach

I believe a better approach is to define a URI scheme for XCM resources to report information about them via Runtime API.
With it, we aren't tied to a particular programming language and its types. Yet, it is still easy to process, as the URI parsing tools are available in basically every programming language. Also, the URI form is easier to type and read if you need to deal with XCM assets more or less directly.

Note: if other XCM Runtime APIs also used the URI, it would've been better because of the uniform and language-independent approach.

So, instead of returning the extra types shown above, the Runtime API would return just a URI and chain-specific metadata:

type Xcmi = String;
type Metadata = String; // A simple string of `(key=value)*`

trait SupportedXcmAssets {
    fn query_supported_assets(version: Version) -> Result<Vec<Xcmi>, Error>;

    // chain-specific info like `is_sufficient`. Useful for UIs
    fn query_asset_metadata(xcmi: Xcmi) -> Result<Metadata, Error>;
}

We might define the scheme as xcmi:// [asset-location] [?fungibility-variant].

The example output of the query_supported_assets(3):

  • DOT:
    xcmi://v3/GlobalConsensus(Polkadot)?fungible
  • USDT:
    xcmi://v3/GlobalConsensus(Polkadot)/Parachain(1000)/PalletInstance(50)/GeneralIndex(1984)?fungible
  • Unique Network's NFT collection 2:
    xcmi://v3/GlobalConsensus(Polkadot)/Parachain(2037)/GeneralIndex(2)?nonfungible=Index(*)
  • Moonbeam's NFT collection of a contract 0x...:
    xcmi://v3/GlobalConsensus(Polkadot)/Parachain(2004)/AccountKey20(0x...)?nonfungible=Array32(*)

This makes it easier to read and use asset information in any language and allows the unification of certain interfaces.

Index(*) and Array32(*) mark the non-fungible ID variant used but don't specify a concrete token. Instead, the * is reported here, notifying the user that the NFT collection is supported regardless of a concrete token.

For instance, look at the query_asset_metadata in the last code snippet.
We can:

  • Query the metadata of a token collection/class:
    query_asset_metadata("xcmi://v3/GlobalConsensus(Polkadot)")
  • Query the tokenUri of Moonbeam's NFT:
    query_asset_metadata("xcmi://v3/GlobalConsensus(Polkadot)/Parachain(2004)/AccountKey20(0x...)?nonfungible=Array32(0x...)")
  • Query the properties of Unique's NFT (collection=2, token=15):
    query_asset_metadata("xcmi://v3/GlobalConsensus(Polkadot)/Parachain(2037)/GeneralIndex(2)?nonfungible=Index(15)")
  • Query the properties of the Unique's collection 2:
    query_asset_metadata("xcmi://v3/GlobalConsensus(Polkadot)/Parachain(2037)/GeneralIndex(2)")

BTW, it would've also been great to have a method for uniformly querying an account's balance.
fn query_balance(account: AccountId, xcmi: Xcmi) -> Result<u128, Error>.

This would work consistently with fungibles and non-fungibles:

  • if a user has an NFT fully identified by the xcmi, then its balance is 1
  • if we identify the NFT collection instead, the balance will equal the number of owned NFTs in that collection.

CC @franciscoaguirre @xlc

Metadata

Metadata

Assignees

No one assigned

    Labels

    T6-XCMThis PR/Issue is related to XCM.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions