Skip to content

XCM Asset Metadata#125

Merged
paritytech-rfc-bot[bot] merged 6 commits intopolkadot-fellows:mainfrom
UniqueNetwork:xcm-asset-metadata
Jan 24, 2025
Merged

XCM Asset Metadata#125
paritytech-rfc-bot[bot] merged 6 commits intopolkadot-fellows:mainfrom
UniqueNetwork:xcm-asset-metadata

Conversation

@mrshiposha
Copy link
Contributor

This is a refined version of the XCM Asset Metadata RFC.

This PR supersedes the PR opened in the xcm-format repository following the migration of XCM RFCs to the Fellowship RFCs.

Summary

This RFC proposes a metadata format for XCM-identifiable assets (i.e., for fungible/non-fungible collections and non-fungible tokens) and a set of instructions to communicate it across chains.

Copy link
Contributor

@acatangiu acatangiu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me!

Co-authored-by: Adrian Catangiu <adrian@parity.io>
@anaelleltd anaelleltd added the Proposed Is awaiting 3 formal reviews. label Nov 3, 2024
Copy link
Member

@xlc xlc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe explicit define MetadataMap and MetadataKeys

type MetadataKey = Vec<u8>;
type MetadataValue = Vec<u8>;
type MetadataMap = BTreeMap<MetadataKey, MetadataValue>;
type MetadataKeys = BTreeSet<MetadataKey>; // or call it MetadataKeySet?

@xlc
Copy link
Member

xlc commented Dec 13, 2024

Let me know if you want me to put this for onchain voting


The `ReportMetadata` can be used without origin (i.e., following the `ClearOrigin` instruction) since it only reads state.

Safety: The reporter origin should be trusted to hold the true metadata. If the reserve-based model is considered, the asset's reserve location must be viewed as the only source of truth about the metadata.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're envisioning this metadata to be useful for fungible assets as well? In that case, would it be something small like name, decimals, symbol?

If that's the case, I guess we only need to have this security measure for NFTs. Runtimes should identify foreign NFTs and not report the metadata for them since they don't hold the source of truth.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're envisioning this metadata to be useful for fungible assets as well? In that case, would it be something small like name, decimals, symbol?

Yes, since Asset { id: ASSET_ID, fun: NonFungible(AssetInstance::Undefined) } now means a collection.
However, we'd need some standards so different fungible implementations could report something universally applicable.

If that's the case, I guess we only need to have this security measure for NFTs.

What about the derivative fungible collections? They might also have a symbol, decimals, etc. However, other chains shouldn't use derivative collections' information as true information (though maybe they might want to inspect such information?).

Runtimes should identify foreign NFTs and not report the metadata for them since they don't hold the source of truth.

True. Yet, a counterparty might act maliciously. I wrote this safety note just to highlight that it is our decision on whom to trust.


Regarding ergonomics, no drawbacks were noticed.

As for the user experience, it could discover new cross-chain use cases involving asset collections and NFTs, indicating a positive impact.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if there are any metadata keys that we can make known and standard instead of leaving everything as a Vec<u8>. But I think this is a good start and cross-chain NFT standards can start to emerge from this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, metadata standards should exist. This is one of the tasks of the NFT Collective (a project launched by Unique Network).
However, this RFC outlines the technical possibility of interchanging data in a universal way via XCM.

@mrshiposha
Copy link
Contributor Author

@xlc Could you please put this RFC for on-chain voting?

@xlc
Copy link
Member

xlc commented Jan 18, 2025

/rfc propose

@paritytech-rfc-bot
Copy link
Contributor

Hey @xlc, here is a link you can use to create the referendum aiming to approve this RFC number 0125.

Instructions
  1. Open the link.

  2. Switch to the Submission tab.

  1. Adjust the transaction if needed (for example, the proposal Origin).

  2. Submit the Transaction


It is based on commit hash d16c0f71119a168e1be0cc4845902658c9ccaad2.

The proposed remark text is: RFC_APPROVE(0125,7e5c5f974903fc550ec31aa565177f22675f1238255dd5d7c89561d33b654b49).

@xlc
Copy link
Member

xlc commented Jan 18, 2025

@github-actions
Copy link

Voting for this referenda is ongoing.

Vote for it here

@mrshiposha
Copy link
Contributor Author

The referendum has been executed.

However, the bot didn't react to this for some reason.

@xlc
Copy link
Member

xlc commented Jan 24, 2025

/rfc process

@paritytech-rfc-bot
Copy link
Contributor

Please provider a block hash where the referendum confirmation event is to be found.
For example:

/rfc process 0x39fbc57d047c71f553aa42824599a7686aea5c9aab4111f6b836d35d3d058162
Instructions to find the block hashHere is one way to find the corresponding block hash.
  1. Open the referendum on Subsquare.

  2. Switch to the Timeline tab.


  1. Go to the details of the Confirmed event.

  1. Go to the details of the block containing that event.

  1. Here you can find the block hash.

@xlc
Copy link
Member

xlc commented Jan 24, 2025

/rfc process 0x06151e3d6507c12dcfa4644062f678b9f9b433d1477ec8b028a9cc8ad70a96c9

@paritytech-rfc-bot paritytech-rfc-bot bot merged commit f346983 into polkadot-fellows:main Jan 24, 2025
@paritytech-rfc-bot
Copy link
Contributor

The on-chain referendum has approved the RFC.

@anaelleltd anaelleltd added Approved Has passed on-chain voting. and removed Proposed Is awaiting 3 formal reviews. labels Jan 24, 2025
github-merge-queue bot pushed a commit to paritytech/polkadot-sdk that referenced this pull request Aug 7, 2025
## Overview

This PR provides new XCM types and tools for building NFT Asset
Transactors.
The new types use general and granular NFT traits from #5620.

The new XCM adapters and utility types to work with NFTs can be
considered the main deliverable of the **[XCM NFT
proposal](https://polkadot.polkassembly.io/referenda/379)**. The new
types use a more general approach, making integration into any chain
with various NFT implementations easier.

For instance, different implementations could use:
* different ID assignment approaches
    * predefined NFT IDs - pallet-uniques, pallet-nfts
* derived NFT IDs (NFT IDs are automatically derived from collection
IDs) - Unique Network, ORML/Acala, Aventus
* classless (collection-less) tokens - CoreTime NFTs, Aventus NFT
Manager NFTs
* in-class (in-collection) tokens - Unique Network, pallet-uniques,
pallet-nfts, ORML/Acala
* different approaches to storing associated data on-chain:
    * data is stored entirely separately from tokens - pallet-uniques
* data is stored partially or entirely within tokens (i.e., burning a
token means destroying it with its data) - pallet-nfts
([partially](https://github.com/paritytech/polkadot-sdk/blob/8b4cfda7589325d1a34f70b3770ab494a9d4052c/substrate/frame/nfts/src/features/create_delete_item.rs#L240-L241)),
Unique Network, ORML/Acala

With new types, these differences can be abstracted away.

Moreover, the new types provide greater flexibility for supporting
derivative NFTs, allowing several possible approaches depending on the
given chain's team's goals or restrictions (see the `pallet-derivatives`
crate docs and mock docs).

Also, this is the PR I mentioned in the
#4073 issue, as it can
be viewed as the solution. In particular, the new adapter
(`UniqueInstancesAdapter`) requires the `Update` operation with the
`ChangeOwnerFrom` strategy. This brings the attention of both a
developer and a reviewer to the `ChangeOwnerFrom` strategy (meaning that
the transfer checks if the asset can be transferred from a given account
to another account), both at trait bound and at the call site, without
sacrificing the flexibility of the NFT traits.

## New types for xcm-builder and xcm-executor

This PR introduces several XCM types.

The `UniqueInstancesAdapter` is a new `TransactAsset` adapter that
supersedes both `NonFungibleAdapter` and `NonFungiblesAdapter` (for
reserve-based transfers only, as teleports can't be implemented
appropriately without transferring the NFT metadata alongside it; no
standard solution exists for that yet. Hopefully, the Felloweship RFC
125 ([PR](polkadot-fellows/RFCs#125),
[text](https://github.com/polkadot-fellows/RFCs/blob/3a24444278f22dac414fbd2b5c4b205f73d78af7/text/0125-xcm-asset-metadata.md))
will help with that).

Thanks to the new Matcher types, the new adapter can be used instead of
both `NonFungibleAdapter` and `NonFungiblesAdapter`:
* `MatchesInstance` (a trait)
* `MatchInClassInstances`
* `MatchClasslessInstances`

The `UniqueInstancesAdapter` works with existing tokens only. To create
new tokens (derivative ones), there is the
`UniqueInstancesDepositAdapter`.

See the `pallet-derivatives` mock and tests for the usage example.

### Superseding the old adapters for pallet-uniques

Here is how the new `UniqueInstancesAdapter` in Westend Asset Hub
replaces the `NonFungiblesAdapter`:

```rust
/// Means for transacting unique assets.
pub type UniquesTransactor = UniqueInstancesAdapter<
	AccountId,
	LocationToAccountId,
	MatchInClassInstances<UniquesConvertedConcreteId>,
	pallet_uniques::asset_ops::Item<Uniques>,
>;
```

`MatchInClassInstances` allows us to reuse the already existing
`UniquesConvertedConcreteId` matcher.
The `pallet_uniques::asset_ops::Item<Uniques>` already implements the
needed traits.

So, migrating from the old adapter to the new one regarding runtime
config changes is easy.

>NOTE: `pallet_uniques::asset_ops::Item` grants access to the asset
operations of NFT items of a given pallet-uniques instance, whereas
`pallet_uniques::asset_ops::Collection` grants access to the collection
operations.

### Declarative modification of an NFT engine

If an NFT-hosting pallet only implements a transfer operation but not
the `Stash` and `Restore`, one could declaratively add them using the
`UniqueInstancesWithStashAccount` adapter.

So, you can use it with the `UniqueInstancesAdapter` as follows:
```rust
parameter_types! {
    	pub StashAccountId: AccountId = /* Some Stash Account ID */;
}

type Transactor = UniqueInstancesAdapter<
	AccountId,
	LocationToAccountId,
	Matcher,
	UniqueInstancesWithStashAccount<StashAccountId, NftEngine>,
>;
```

### Supporting derivative NFTs (reserve-based model)

There are several possible scenarios of supporting derivative NFTs (and
their collections, if applicable).

A separate NFT-hosting pallet instance could be configured to use XCM
`AssetId` as collection ID and `AssetInstance` token ID. In that case,
the asset transaction doesn't need any special treatment.

However, registering NFT collections might require a special API.
Also, if the NFT-hosting pallet can't be configured to use XCM ID types,
we would need to store a mapping between the XCM ID of the original
token and the derivative ID.

See the `pallet-derivatives` crate docs for details.
The example of its usage can be found in its mock and tests.

#### TODO

No benchmarks were run for `pallet-derivatives`, so there is no
`weights.rs` for it yet.

---------

Co-authored-by: Branislav Kontur <bkontur@gmail.com>
Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com>
@mrshiposha
Copy link
Contributor Author

NOTE on implementation: this can't be implemented before introducing a basic structure of XCM v6 (effectively, this issue must be resolved first)

alvicsam pushed a commit to paritytech/polkadot-sdk that referenced this pull request Oct 17, 2025
## Overview

This PR provides new XCM types and tools for building NFT Asset
Transactors.
The new types use general and granular NFT traits from #5620.

The new XCM adapters and utility types to work with NFTs can be
considered the main deliverable of the **[XCM NFT
proposal](https://polkadot.polkassembly.io/referenda/379)**. The new
types use a more general approach, making integration into any chain
with various NFT implementations easier.

For instance, different implementations could use:
* different ID assignment approaches
    * predefined NFT IDs - pallet-uniques, pallet-nfts
* derived NFT IDs (NFT IDs are automatically derived from collection
IDs) - Unique Network, ORML/Acala, Aventus
* classless (collection-less) tokens - CoreTime NFTs, Aventus NFT
Manager NFTs
* in-class (in-collection) tokens - Unique Network, pallet-uniques,
pallet-nfts, ORML/Acala
* different approaches to storing associated data on-chain:
    * data is stored entirely separately from tokens - pallet-uniques
* data is stored partially or entirely within tokens (i.e., burning a
token means destroying it with its data) - pallet-nfts
([partially](https://github.com/paritytech/polkadot-sdk/blob/8b4cfda7589325d1a34f70b3770ab494a9d4052c/substrate/frame/nfts/src/features/create_delete_item.rs#L240-L241)),
Unique Network, ORML/Acala

With new types, these differences can be abstracted away.

Moreover, the new types provide greater flexibility for supporting
derivative NFTs, allowing several possible approaches depending on the
given chain's team's goals or restrictions (see the `pallet-derivatives`
crate docs and mock docs).

Also, this is the PR I mentioned in the
#4073 issue, as it can
be viewed as the solution. In particular, the new adapter
(`UniqueInstancesAdapter`) requires the `Update` operation with the
`ChangeOwnerFrom` strategy. This brings the attention of both a
developer and a reviewer to the `ChangeOwnerFrom` strategy (meaning that
the transfer checks if the asset can be transferred from a given account
to another account), both at trait bound and at the call site, without
sacrificing the flexibility of the NFT traits.

## New types for xcm-builder and xcm-executor

This PR introduces several XCM types.

The `UniqueInstancesAdapter` is a new `TransactAsset` adapter that
supersedes both `NonFungibleAdapter` and `NonFungiblesAdapter` (for
reserve-based transfers only, as teleports can't be implemented
appropriately without transferring the NFT metadata alongside it; no
standard solution exists for that yet. Hopefully, the Felloweship RFC
125 ([PR](polkadot-fellows/RFCs#125),
[text](https://github.com/polkadot-fellows/RFCs/blob/3a24444278f22dac414fbd2b5c4b205f73d78af7/text/0125-xcm-asset-metadata.md))
will help with that).

Thanks to the new Matcher types, the new adapter can be used instead of
both `NonFungibleAdapter` and `NonFungiblesAdapter`:
* `MatchesInstance` (a trait)
* `MatchInClassInstances`
* `MatchClasslessInstances`

The `UniqueInstancesAdapter` works with existing tokens only. To create
new tokens (derivative ones), there is the
`UniqueInstancesDepositAdapter`.

See the `pallet-derivatives` mock and tests for the usage example.

### Superseding the old adapters for pallet-uniques

Here is how the new `UniqueInstancesAdapter` in Westend Asset Hub
replaces the `NonFungiblesAdapter`:

```rust
/// Means for transacting unique assets.
pub type UniquesTransactor = UniqueInstancesAdapter<
	AccountId,
	LocationToAccountId,
	MatchInClassInstances<UniquesConvertedConcreteId>,
	pallet_uniques::asset_ops::Item<Uniques>,
>;
```

`MatchInClassInstances` allows us to reuse the already existing
`UniquesConvertedConcreteId` matcher.
The `pallet_uniques::asset_ops::Item<Uniques>` already implements the
needed traits.

So, migrating from the old adapter to the new one regarding runtime
config changes is easy.

>NOTE: `pallet_uniques::asset_ops::Item` grants access to the asset
operations of NFT items of a given pallet-uniques instance, whereas
`pallet_uniques::asset_ops::Collection` grants access to the collection
operations.

### Declarative modification of an NFT engine

If an NFT-hosting pallet only implements a transfer operation but not
the `Stash` and `Restore`, one could declaratively add them using the
`UniqueInstancesWithStashAccount` adapter.

So, you can use it with the `UniqueInstancesAdapter` as follows:
```rust
parameter_types! {
    	pub StashAccountId: AccountId = /* Some Stash Account ID */;
}

type Transactor = UniqueInstancesAdapter<
	AccountId,
	LocationToAccountId,
	Matcher,
	UniqueInstancesWithStashAccount<StashAccountId, NftEngine>,
>;
```

### Supporting derivative NFTs (reserve-based model)

There are several possible scenarios of supporting derivative NFTs (and
their collections, if applicable).

A separate NFT-hosting pallet instance could be configured to use XCM
`AssetId` as collection ID and `AssetInstance` token ID. In that case,
the asset transaction doesn't need any special treatment.

However, registering NFT collections might require a special API.
Also, if the NFT-hosting pallet can't be configured to use XCM ID types,
we would need to store a mapping between the XCM ID of the original
token and the derivative ID.

See the `pallet-derivatives` crate docs for details.
The example of its usage can be found in its mock and tests.

#### TODO

No benchmarks were run for `pallet-derivatives`, so there is no
`weights.rs` for it yet.

---------

Co-authored-by: Branislav Kontur <bkontur@gmail.com>
Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Approved Has passed on-chain voting.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants