From e7003fd5909e1e83a72a1d5b9010dbbd446fcbf9 Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Thu, 25 Apr 2024 17:46:17 +0200 Subject: [PATCH 01/41] feat(frame-support): add asset ops --- substrate/frame/support/src/traits/tokens.rs | 1 + .../support/src/traits/tokens/asset_ops.rs | 543 ++++++++++++++++++ 2 files changed, 544 insertions(+) create mode 100644 substrate/frame/support/src/traits/tokens/asset_ops.rs diff --git a/substrate/frame/support/src/traits/tokens.rs b/substrate/frame/support/src/traits/tokens.rs index 8842b20580181..53404f7398546 100644 --- a/substrate/frame/support/src/traits/tokens.rs +++ b/substrate/frame/support/src/traits/tokens.rs @@ -17,6 +17,7 @@ //! Traits for working with tokens and their associated datastructures. +pub mod asset_ops; pub mod currency; pub mod fungible; pub mod fungibles; diff --git a/substrate/frame/support/src/traits/tokens/asset_ops.rs b/substrate/frame/support/src/traits/tokens/asset_ops.rs new file mode 100644 index 0000000000000..f44ec23366996 --- /dev/null +++ b/substrate/frame/support/src/traits/tokens/asset_ops.rs @@ -0,0 +1,543 @@ +//! Abstract asset operations traits. +//! +//! The following operations are defined: +//! * [`InspectMetadata`] +//! * [`UpdateMetadata`] +//! * [`Create`] +//! * [`Transfer`] +//! * [`Destroy`] +//! +//! Also, all the operations above (except the `Create` operation) use +//! the [`AssetDefinition`] to retrieve the `Id` type of the asset. +//! +//! Each asset operation can be implemented for different asset kinds +//! such as [`Class`](common_asset_kinds::Class) and [`Instance`](common_asset_kinds::Instance). +//! +//! Also, an asset operation can be implemented multiple times +//! using different strategies associated with this operation. +//! +//! A strategy defines the operation behavior, +//! may supply additional parameters, +//! and may define a return value type of the operation. + +use crate::dispatch::DispatchResult; +use core::marker::PhantomData; +use sp_runtime::DispatchError; +use sp_std::vec::Vec; + +/// Trait for defining an asset of a certain kind. +/// The definition must provide the `Id` type to identify the asset. +/// +/// The common asset kinds are: +/// * The [`Class`](common_asset_kinds::Class) asset kind is of assets that resemble class-like +/// entities. For example, a collection of non-fungible tokens belongs to this kind. +/// * The [`Instance`](common_asset_kinds::Instance) asset kind is of assets that resemble concrete +/// instances of something. For example, a non-fungible token (which may or may not be part of a +/// certain class) belongs to this kind. +/// +/// Other asset kinds can be defined. +pub trait AssetDefinition { + /// Type for identifying the asset. + type Id; +} + +/// A strategy for use in the [`InspectMetadata`] implementations. +/// +/// The common inspect strategies are: +/// * [`Bytes`](common_strategies::Bytes) +/// * [`Ownership`](common_strategies::Ownership) +/// * [`CanCreate`](common_strategies::CanCreate) +/// * [`CanTransfer`](common_strategies::CanTransfer) +/// * [`CanDestroy`](common_strategies::CanDestroy) +/// * [`CanUpdateMetadata`](common_strategies::CanUpdateMetadata) +pub trait MetadataInspectStrategy { + /// The type to return from the [`InspectMetadata::inspect_metadata`] function. + type Value; +} + +/// A trait representing the ability of a certain asset kind to **provide** its metadata +/// information. +/// +/// This trait can be implemented multiple times using different [`inspect +/// strategies`](MetadataInspectStrategy). +/// +/// An inspect strategy defines how the asset metadata is identified/retrieved +/// and what [`Value`](MetadataInspectStrategy::Value) type is returned. +pub trait InspectMetadata: + AssetDefinition +{ + /// Inspect metadata information of the asset + /// using the given `id` and the inspect `strategy`. + /// + /// The ID type is retrieved from the [`AssetDefinition`]. + fn inspect_metadata( + id: &Self::Id, + strategy: Strategy, + ) -> Result; +} + +/// A strategy for use in the [`UpdateMetadata`] implementations. +/// +/// The common update strategies are: +/// * [`Bytes`](common_strategies::Bytes) +/// * [`CanCreate`](common_strategies::CanCreate) +/// * [`CanTransfer`](common_strategies::CanTransfer) +/// * [`CanDestroy`](common_strategies::CanDestroy) +/// * [`CanUpdateMetadata`](common_strategies::CanUpdateMetadata) +pub trait MetadataUpdateStrategy { + /// The type of metadata update to accept in the [`UpdateMetadata::update_metadata`] function. + type Update<'u>; +} + +/// A trait representing the ability of a certain asset kind to **update** its metadata information. +/// +/// This trait can be implemented multiple times using different [`update +/// strategies`](MetadataUpdateStrategy). +/// +/// An update strategy defines how the asset metadata is identified +/// and what [`Update`](MetadataUpdateStrategy::Update) type is used. +pub trait UpdateMetadata: + AssetDefinition +{ + /// Update metadata information of the asset + /// using the given `id`, the update `strategy`, and the `update` value. + /// + /// The ID type is retrieved from the [`AssetDefinition`]. + fn update_metadata( + id: &Self::Id, + strategy: Strategy, + update: Strategy::Update<'_>, + ) -> DispatchResult; +} + +/// A strategy for use in the [`Create`] implementations. +/// +/// The common "create" strategies are: +/// * [`Owned`](common_strategies::Owned) +/// * [`Adminable`](common_strategies::Adminable) +pub trait CreateStrategy { + /// This type represents successful asset creation. + /// It will be the return type of the [`Create::create`] function. + type Success; +} + +/// An ID assignment approach to use in the "create" strategies. +/// +/// The common ID assignments are: +/// * [`AutoId`](common_strategies::AutoId) +/// * [`PredefinedId`](common_strategies::PredefinedId) +/// * [`DeriveIdFrom`](common_strategies::DeriveIdFrom) +pub trait IdAssignment { + /// The reported ID type. + /// + /// Examples: + /// * [`AutoId`](common_strategies::AutoId) returns ID of the newly created asset + /// * [`PredefinedId`](common_strategies::PredefinedId) returns `()` since the ID is already + /// defined + /// * [`DeriveIdFrom`](common_strategies::DeriveIdFrom) returns the derived ID + type ReportedId; +} + +/// A trait representing the ability of a certain asset kind to be created. +/// +/// This trait can be implemented multiple times using different [`"create" +/// strategies`](CreateStrategy). +/// +/// A create strategy defines all aspects of asset creation including how an asset ID is assigned. +pub trait Create { + /// Create a new asset using the provided `strategy`. + fn create(strategy: Strategy) -> Result; +} + +/// A strategy for use in the [`Transfer`] implementations. +/// +/// The common transfer strategies are: +/// * [`JustTo`](common_strategies::JustTo) +/// * [`FromTo`](common_strategies::FromTo) +pub trait TransferStrategy {} + +/// A trait representing the ability of a certain asset kind to be transferred. +/// +/// This trait can be implemented multiple times using different [`transfer +/// strategies`](TransferStrategy). +/// +/// A transfer strategy defines transfer parameters. +pub trait Transfer: AssetDefinition { + /// Transfer the asset identified by the given `id` using the provided `strategy`. + /// + /// The ID type is retrieved from the [`AssetDefinition`]. + fn transfer(id: &Self::Id, strategy: Strategy) -> DispatchResult; +} + +/// A strategy for use in the [`Destroy`] implementations. +/// +/// The common destroy strategies are: +/// * [`JustDestroy`](common_strategies::JustDestroy) +/// * [`IfOwnedBy`](common_strategies::IfOwnedBy) +/// * [`WithWitness`](common_strategies::WithWitness) +/// * [`IfOwnedByWithWitness`](common_strategies::IfOwnedByWithWitness) +pub trait DestroyStrategy { + /// This type represents successful asset destruction. + /// It will be the return type of the [`Destroy::destroy`] function. + type Success; +} + +/// A trait representing the ability of a certain asset kind to be destroyed. +/// +/// This trait can be implemented multiple times using different [`destroy +/// strategies`](DestroyStrategy). +/// +/// A destroy strategy defines destroy parameters and the result value type. +pub trait Destroy: AssetDefinition { + /// Destroy the asset identified by the given `id` using the provided `strategy`. + /// + /// The ID type is retrieved from the [`AssetDefinition`]. + fn destroy(id: &Self::Id, strategy: Strategy) -> Result; +} + +/// This modules contains the common asset kinds. +pub mod common_asset_kinds { + /// The `Class` asset kind is of assets that resemble class-like entities. + /// For instance, a collection of non-fungible tokens is an asset of this kind. + pub struct Class; + + /// The `Instance` asset kind represents assets resembling instances of something. + /// For instance, a single non-fungible token is an asset of this kind. + /// + /// An instance asset is not necessarily bound to a class. + /// There could be "classless" instances. + pub struct Instance; +} + +/// This modules contains the common asset ops strategies. +pub mod common_strategies { + use super::*; + + /// The `WithOrigin` is a strategy that accepts a runtime origin and the `Inner` strategy. + /// + /// It is meant to be used when the origin check should be performed + /// in addition to the `Inner` strategy. + /// + /// The `WithOrigin` implements any strategy that the `Inner` implements. + pub struct WithOrigin(pub RuntimeOrigin, pub Inner); + impl MetadataInspectStrategy + for WithOrigin + { + type Value = Inner::Value; + } + impl MetadataUpdateStrategy + for WithOrigin + { + type Update<'u> = Inner::Update<'u>; + } + impl CreateStrategy for WithOrigin { + type Success = Inner::Success; + } + impl TransferStrategy for WithOrigin {} + impl DestroyStrategy for WithOrigin { + type Success = Inner::Success; + } + + /// The `Bytes` strategy represents raw metadata bytes. + /// It is both an [inspect](MetadataInspectStrategy) and [update](MetadataUpdateStrategy) + /// metadata strategy. + /// + /// * As the inspect strategy, it returns `Vec`. + /// * As the update strategy, it accepts `Option<&[u8]>`, where `None` means data removal. + /// + /// By default, the `Bytes` identifies a byte blob associated with the asset (the only one + /// blob). However, a user can define several flavors of this strategy by supplying the `Flavor` + /// type. The `Flavor` type can also contain additional data (like a byte key) to identify a + /// certain byte data. + pub struct Bytes(pub Flavor); + impl Default for Bytes<()> { + fn default() -> Self { + Self(()) + } + } + impl MetadataInspectStrategy for Bytes { + type Value = Vec; + } + impl MetadataUpdateStrategy for Bytes { + type Update<'u> = Option<&'u [u8]>; + } + + /// The `Ownership` [inspect](MetadataInspectStrategy) metadata strategy allows getting the + /// owner of an asset. + pub struct Ownership(PhantomData); + impl Default for Ownership { + fn default() -> Self { + Self(PhantomData) + } + } + impl MetadataInspectStrategy for Ownership { + type Value = Owner; + } + + /// The `CanCreate` strategy represents the ability to create an asset. + /// It is both an [inspect](MetadataInspectStrategy) and [update](MetadataUpdateStrategy) + /// metadata strategy. + /// + /// * As the inspect strategy, it returns `bool`. + /// * As the update strategy is accepts `bool`. + /// + /// By default, this strategy means the ability to create an asset "in general". + /// However, a user can define several flavors of this strategy by supplying the `Flavor` type. + /// The `Flavor` type can add more details to the strategy. + /// For instance, "Can **a specific user** create an asset?". + pub struct CanCreate(pub Flavor); + impl Default for CanCreate<()> { + fn default() -> Self { + Self(()) + } + } + impl MetadataInspectStrategy for CanCreate { + type Value = bool; + } + impl MetadataUpdateStrategy for CanCreate { + type Update<'u> = bool; + } + + /// The `CanTransfer` strategy represents the ability to transfer an asset. + /// It is both an [inspect](MetadataInspectStrategy) and [update](MetadataUpdateStrategy) + /// metadata strategy. + /// + /// * As the inspect strategy, it returns `bool`. + /// * As the update strategy is accepts `bool`. + /// + /// By default, this strategy means the ability to transfer an asset "in general". + /// However, a user can define several flavors of this strategy by supplying the `Flavor` type. + /// The `Flavor` type can add more details to the strategy. + /// For instance, "Can **a specific user** transfer an asset of **another user**?". + pub struct CanTransfer(pub Flavor); + impl Default for CanTransfer<()> { + fn default() -> Self { + Self(()) + } + } + impl MetadataInspectStrategy for CanTransfer { + type Value = bool; + } + impl MetadataUpdateStrategy for CanTransfer { + type Update<'u> = bool; + } + + /// The `CanDestroy` strategy represents the ability to destroy an asset. + /// It is both an [inspect](MetadataInspectStrategy) and [update](MetadataUpdateStrategy) + /// metadata strategy. + /// + /// * As the inspect strategy, it returns `bool`. + /// * As the update strategy is accepts `bool`. + /// + /// By default, this strategy means the ability to destroy an asset "in general". + /// However, a user can define several flavors of this strategy by supplying the `Flavor` type. + /// The `Flavor` type can add more details to the strategy. + /// For instance, "Can **a specific user** destroy an asset of **another user**?". + pub struct CanDestroy(pub Flavor); + impl Default for CanDestroy<()> { + fn default() -> Self { + Self(()) + } + } + impl MetadataInspectStrategy for CanDestroy { + type Value = bool; + } + impl MetadataUpdateStrategy for CanDestroy { + type Update<'u> = bool; + } + + /// The `CanUpdateMetadata` strategy represents the ability to update the metadata of an asset. + /// It is both an [inspect](MetadataInspectStrategy) and [update](MetadataUpdateStrategy) + /// metadata strategy. + /// + /// * As the inspect strategy, it returns `bool`. + /// * As the update strategy is accepts `bool`. + /// + /// By default, this strategy means the ability to update the metadata of an asset "in general". + /// However, a user can define several flavors of this strategy by supplying the `Flavor` type. + /// The `Flavor` type can add more details to the strategy. + /// For instance, "Can **a specific user** update the metadata of an asset **under a certain + /// key**?". + pub struct CanUpdateMetadata(pub Flavor); + impl Default for CanUpdateMetadata<()> { + fn default() -> Self { + Self(()) + } + } + impl MetadataInspectStrategy for CanUpdateMetadata { + type Value = bool; + } + impl MetadataUpdateStrategy for CanUpdateMetadata { + type Update<'u> = bool; + } + + /// The `AutoId` is an ID assignment approach intended to be used in [`"create" + /// strategies`](CreateStrategy). + /// + /// It accepts the `Id` type of the asset. + /// The "create" strategy should report the value of type `Id` upon successful asset creation. + pub struct AutoId(PhantomData); + impl AutoId { + pub fn new() -> Self { + Self(PhantomData) + } + } + impl IdAssignment for AutoId { + type ReportedId = Id; + } + + /// The `PredefinedId` is an ID assignment approach intended to be used in [`"create" + /// strategies`](CreateStrategy). + /// + /// It accepts a value of the `Id` type. + /// The "create" strategy should use the provided ID value to create a new asset. + pub struct PredefinedId<'a, Id>(pub &'a Id); + impl<'a, Id> IdAssignment for PredefinedId<'a, Id> { + type ReportedId = (); + } + + /// The `DeriveIdFrom` is an ID assignment approach intended to be used in [`"create" + /// strategies`](CreateStrategy). + /// + /// It accepts the `ParentId` and the `ChildId`. + /// The `ChildId` value should be computed by the "create" strategy using the `ParentId` value. + /// + /// The "create" strategy should report the `ChildId` value upon successful asset creation. + /// + /// An example of ID derivation is the creation of an NFT inside a collection using the + /// collection ID. The child ID in this case is the full ID of the NFT. + pub struct DeriveIdFrom<'a, ParentId, ChildId>(pub &'a ParentId, PhantomData); + impl<'a, ParentId, ChildId> DeriveIdFrom<'a, ParentId, ChildId> { + pub fn parent_id(primary_id: &'a ParentId) -> Self { + Self(primary_id, PhantomData) + } + } + impl<'a, ParentId, ChildId> IdAssignment for DeriveIdFrom<'a, ParentId, ChildId> { + type ReportedId = ChildId; + } + + /// The `Owned` is a [`"create" strategy`](CreateStrategy). + /// + /// It accepts: + /// * The [ID assignment](IdAssignment) approach + /// * The `owner` + /// * The optional `config` + /// * The optional creation `witness` + /// + /// The [`Success`](CreateStrategy::Success) will contain + /// the [reported ID](IdAssignment::ReportedId) of the ID assignment approach. + pub struct Owned<'a, Assignment: IdAssignment, Owner, Config = (), Witness = ()> { + pub id_assignment: Assignment, + pub owner: &'a Owner, + pub config: &'a Config, + pub witness: &'a Witness, + } + impl<'a, Assignment: IdAssignment, Owner> Owned<'a, Assignment, Owner, (), ()> { + pub fn new(id_assignment: Assignment, owner: &'a Owner) -> Self { + Self { id_assignment, owner, config: &(), witness: &() } + } + } + impl<'a, Assignment: IdAssignment, Owner, Config> Owned<'a, Assignment, Owner, Config, ()> { + pub fn new_configured( + id_assignment: Assignment, + owner: &'a Owner, + config: &'a Config, + ) -> Self { + Self { id_assignment, owner, config, witness: &() } + } + } + impl<'a, Assignment: IdAssignment, Owner, Config, Witness> CreateStrategy + for Owned<'a, Assignment, Owner, Config, Witness> + { + type Success = Assignment::ReportedId; + } + + /// The `Adminable` is a [`"create" strategy`](CreateStrategy). + /// + /// It accepts: + /// * The [ID assignment](IdAssignment) approach + /// * The `owner` + /// * The `admin` + /// * The optional `config` + /// * The optional creation `witness` + /// + /// The [`Success`](CreateStrategy::Success) will contain + /// the [reported ID](IdAssignment::ReportedId) of the ID assignment approach. + pub struct Adminable<'a, Assignment: IdAssignment, Account, Config = (), Witness = ()> { + pub id_assignment: Assignment, + pub owner: &'a Account, + pub admin: &'a Account, + pub config: &'a Config, + pub witness: &'a Witness, + } + impl<'a, Assignment: IdAssignment, Account> Adminable<'a, Assignment, Account, (), ()> { + pub fn new(id_assignment: Assignment, owner: &'a Account, admin: &'a Account) -> Self { + Self { id_assignment, owner, admin, config: &(), witness: &() } + } + } + impl<'a, Assignment: IdAssignment, Account, Config> Adminable<'a, Assignment, Account, Config, ()> { + pub fn new_configured( + id_assignment: Assignment, + owner: &'a Account, + admin: &'a Account, + config: &'a Config, + ) -> Self { + Self { id_assignment, owner, admin, config, witness: &() } + } + } + impl<'a, Assignment: IdAssignment, Account, Config, Witness> CreateStrategy + for Adminable<'a, Assignment, Account, Config, Witness> + { + type Success = Assignment::ReportedId; + } + + /// The `JustTo` is a [`transfer strategy`](TransferStrategy). + /// + /// It accepts the target of the transfer, + /// i.e., who will become the asset's owner after the transfer. + pub struct JustTo<'a, Owner>(pub &'a Owner); + impl<'a, Owner> TransferStrategy for JustTo<'a, Owner> {} + + /// The `FromTo` is a [`transfer strategy`](TransferStrategy). + /// + /// It accepts two parameters: `from` and `to` whom the asset should be transferred. + pub struct FromTo<'a, Owner>(pub &'a Owner, pub &'a Owner); + impl<'a, Owner> TransferStrategy for FromTo<'a, Owner> {} + + /// The `JustDestroy` is a [`destroy strategy`](DestroyStrategy). + /// + /// It represents an "unchecked" destruction of the asset. + pub struct JustDestroy; + impl DestroyStrategy for JustDestroy { + type Success = (); + } + + /// The `IfOwnedBy` is a [`destroy strategy`](DestroyStrategy). + /// + /// It accepts a possible owner of the asset. + /// If the provided entity owns the asset, it will be destroyed. + pub struct IfOwnedBy<'a, Owner>(pub &'a Owner); + impl<'a, Owner> DestroyStrategy for IfOwnedBy<'a, Owner> { + type Success = (); + } + + /// The `WithWitness` is a [`destroy strategy`](DestroyStrategy). + /// + /// It accepts a `Witness` to destroy an asset. + /// It will also return a `Witness` value upon destruction. + pub struct WithWitness<'a, Witness>(pub &'a Witness); + impl<'a, Witness> DestroyStrategy for WithWitness<'a, Witness> { + type Success = Witness; + } + + /// The `IfOwnedByWithWitness` is a [`destroy strategy`](DestroyStrategy). + /// + /// It is a combination of the [`IfOwnedBy`] and the [`WithWitness`] strategies. + pub struct IfOwnedByWithWitness<'a, Owner, Witness> { + pub owner: &'a Owner, + pub witness: &'a Witness, + } + impl<'a, Owner, Witness> DestroyStrategy for IfOwnedByWithWitness<'a, Owner, Witness> { + type Success = Witness; + } +} From 6c6cf4b1a98825e76350bf4fb8e01f3cf8ccba3d Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Thu, 25 Apr 2024 17:53:17 +0200 Subject: [PATCH 02/41] feat(pallet-uniques): implement asset ops --- .../frame/uniques/src/impl_asset_ops/class.rs | 180 +++++++++++++++ .../uniques/src/impl_asset_ops/instance.rs | 205 ++++++++++++++++++ .../frame/uniques/src/impl_asset_ops/mod.rs | 2 + substrate/frame/uniques/src/lib.rs | 9 + substrate/frame/uniques/src/types.rs | 4 + 5 files changed, 400 insertions(+) create mode 100644 substrate/frame/uniques/src/impl_asset_ops/class.rs create mode 100644 substrate/frame/uniques/src/impl_asset_ops/instance.rs create mode 100644 substrate/frame/uniques/src/impl_asset_ops/mod.rs diff --git a/substrate/frame/uniques/src/impl_asset_ops/class.rs b/substrate/frame/uniques/src/impl_asset_ops/class.rs new file mode 100644 index 0000000000000..9e64dc4a2ed61 --- /dev/null +++ b/substrate/frame/uniques/src/impl_asset_ops/class.rs @@ -0,0 +1,180 @@ +use crate::{asset_strategies::Attribute, *}; +use frame_support::{ + dispatch::DispatchResult, + ensure, + traits::{ + tokens::asset_ops::{ + common_asset_kinds::Class, + common_strategies::{ + Adminable, Bytes, IfOwnedByWithWitness, Ownership, PredefinedId, WithOrigin, + WithWitness, + }, + AssetDefinition, Create, Destroy, InspectMetadata, + }, + EnsureOrigin, Get, + }, + BoundedSlice, +}; +use frame_system::ensure_signed; +use sp_runtime::DispatchError; + +impl, I: 'static> AssetDefinition for Pallet { + type Id = T::CollectionId; +} + +impl, I: 'static> InspectMetadata> for Pallet { + fn inspect_metadata( + collection: &Self::Id, + _ownership: Ownership, + ) -> Result { + Collection::::get(collection) + .map(|a| a.owner) + .ok_or(Error::::UnknownCollection.into()) + } +} + +impl, I: 'static> InspectMetadata for Pallet { + fn inspect_metadata(collection: &Self::Id, _bytes: Bytes) -> Result, DispatchError> { + CollectionMetadataOf::::get(collection) + .map(|m| m.data.into()) + .ok_or(Error::::NoMetadata.into()) + } +} + +impl<'a, T: Config, I: 'static> InspectMetadata>> for Pallet { + fn inspect_metadata( + collection: &Self::Id, + strategy: Bytes, + ) -> Result, DispatchError> { + let Bytes(Attribute(attribute)) = strategy; + + let attribute = + BoundedSlice::try_from(attribute).map_err(|_| Error::::WrongAttribute)?; + crate::Attribute::::get((collection, Option::::None, attribute)) + .map(|a| a.0.into()) + .ok_or(Error::::AttributeNotFound.into()) + } +} + +impl<'a, T: Config, I: 'static> + Create, T::AccountId>> for Pallet +{ + fn create( + strategy: Adminable, T::AccountId>, + ) -> DispatchResult { + let Adminable { id_assignment: PredefinedId(collection), owner, admin, .. } = strategy; + + Self::do_create_collection( + collection.clone(), + owner.clone(), + admin.clone(), + T::CollectionDeposit::get(), + false, + Event::Created { + collection: collection.clone(), + creator: owner.clone(), + owner: admin.clone(), + }, + ) + } +} + +impl<'a, T: Config, I: 'static> + Create< + Class, + WithOrigin< + T::RuntimeOrigin, + Adminable<'a, PredefinedId<'a, T::CollectionId>, T::AccountId>, + >, + > for Pallet +{ + fn create( + strategy: WithOrigin< + T::RuntimeOrigin, + Adminable, T::AccountId>, + >, + ) -> DispatchResult { + let WithOrigin( + origin, + creation @ Adminable { id_assignment: PredefinedId(collection), owner, .. }, + ) = strategy; + + let maybe_check_signer = + T::ForceOrigin::try_origin(origin).map(|_| None).or_else(|origin| { + T::CreateOrigin::ensure_origin(origin, &collection) + .map(Some) + .map_err(DispatchError::from) + })?; + + if let Some(signer) = maybe_check_signer { + ensure!(signer == *owner, Error::::NoPermission); + } + + >::create(creation) + } +} + +impl<'a, T: Config, I: 'static> Destroy> + for Pallet +{ + fn destroy( + collection: &Self::Id, + strategy: WithWitness, + ) -> Result { + let WithWitness(witness) = strategy; + + Self::do_destroy_collection(collection.clone(), *witness, None) + } +} + +impl<'a, T: Config, I: 'static> + Destroy>> for Pallet +{ + fn destroy( + collection: &Self::Id, + strategy: WithOrigin>, + ) -> Result { + let WithOrigin(origin, destroy) = strategy; + + T::ForceOrigin::ensure_origin(origin)?; + + >::destroy(collection, destroy) + } +} + +impl<'a, T: Config, I: 'static> + Destroy> for Pallet +{ + fn destroy( + collection: &Self::Id, + strategy: IfOwnedByWithWitness, + ) -> Result { + let IfOwnedByWithWitness { owner, witness } = strategy; + + Self::do_destroy_collection(collection.clone(), *witness, Some(owner.clone())) + } +} + +impl<'a, T: Config, I: 'static> + Destroy< + Class, + WithOrigin>, + > for Pallet +{ + fn destroy( + collection: &Self::Id, + strategy: WithOrigin>, + ) -> Result { + let WithOrigin(origin, IfOwnedByWithWitness { owner, witness }) = strategy; + let maybe_check_owner = match T::ForceOrigin::try_origin(origin) { + Ok(_) => None, + Err(origin) => Some(ensure_signed(origin)?), + }; + + if let Some(signer) = maybe_check_owner { + ensure!(signer == *owner, Error::::NoPermission); + } + + Self::do_destroy_collection(collection.clone(), *witness, Some(owner.clone())) + } +} diff --git a/substrate/frame/uniques/src/impl_asset_ops/instance.rs b/substrate/frame/uniques/src/impl_asset_ops/instance.rs new file mode 100644 index 0000000000000..ad55a4efc662d --- /dev/null +++ b/substrate/frame/uniques/src/impl_asset_ops/instance.rs @@ -0,0 +1,205 @@ +use crate::{asset_strategies::Attribute, *}; +use frame_support::{ + dispatch::DispatchResult, + ensure, + traits::tokens::asset_ops::{ + common_asset_kinds::Instance, + common_strategies::{ + Bytes, CanTransfer, FromTo, IfOwnedBy, JustDestroy, JustTo, Owned, Ownership, + PredefinedId, WithOrigin, + }, + AssetDefinition, Create, Destroy, InspectMetadata, Transfer, + }, + BoundedSlice, +}; +use frame_system::ensure_signed; +use sp_runtime::DispatchError; + +impl, I: 'static> AssetDefinition for Pallet { + type Id = (T::CollectionId, T::ItemId); +} + +impl, I: 'static> InspectMetadata> for Pallet { + fn inspect_metadata( + (collection, item): &Self::Id, + _ownership: Ownership, + ) -> Result { + Item::::get(collection, item) + .map(|a| a.owner) + .ok_or(Error::::UnknownItem.into()) + } +} + +impl, I: 'static> InspectMetadata for Pallet { + fn inspect_metadata( + (collection, item): &Self::Id, + _bytes: Bytes, + ) -> Result, DispatchError> { + ItemMetadataOf::::get(collection, item) + .map(|m| m.data.into()) + .ok_or(Error::::NoMetadata.into()) + } +} + +impl<'a, T: Config, I: 'static> InspectMetadata>> + for Pallet +{ + fn inspect_metadata( + (collection, item): &Self::Id, + strategy: Bytes, + ) -> Result, DispatchError> { + let Bytes(Attribute(attribute)) = strategy; + + let attribute = + BoundedSlice::try_from(attribute).map_err(|_| Error::::WrongAttribute)?; + crate::Attribute::::get((collection, Some(item), attribute)) + .map(|a| a.0.into()) + .ok_or(Error::::AttributeNotFound.into()) + } +} + +impl, I: 'static> InspectMetadata for Pallet { + fn inspect_metadata( + (collection, item): &Self::Id, + _can_transfer: CanTransfer, + ) -> Result { + match (Collection::::get(collection), Item::::get(collection, item)) { + (Some(cd), Some(id)) => Ok(!cd.is_frozen && !id.is_frozen), + _ => Err(Error::::UnknownItem.into()), + } + } +} + +impl<'a, T: Config, I: 'static> + Create, T::AccountId>> + for Pallet +{ + fn create( + strategy: Owned, T::AccountId>, + ) -> DispatchResult { + let Owned { id_assignment: PredefinedId((collection, item)), owner, .. } = strategy; + + Self::do_mint(collection.clone(), *item, owner.clone(), |_| Ok(())) + } +} + +impl<'a, T: Config, I: 'static> + Create< + Instance, + WithOrigin< + T::RuntimeOrigin, + Owned<'a, PredefinedId<'a, (T::CollectionId, T::ItemId)>, T::AccountId>, + >, + > for Pallet +{ + fn create( + strategy: WithOrigin< + T::RuntimeOrigin, + Owned, T::AccountId>, + >, + ) -> DispatchResult { + let WithOrigin( + origin, + Owned { id_assignment: PredefinedId((collection, item)), owner, .. }, + ) = strategy; + + let signer = ensure_signed(origin)?; + + Self::do_mint(collection.clone(), *item, owner.clone(), |collection_details| { + ensure!(collection_details.issuer == signer, Error::::NoPermission); + Ok(()) + }) + } +} + +impl<'a, T: Config, I: 'static> Transfer> for Pallet { + fn transfer((collection, item): &Self::Id, strategy: JustTo) -> DispatchResult { + let JustTo(dest) = strategy; + + Self::do_transfer(collection.clone(), *item, dest.clone(), |_, _| Ok(())) + } +} + +impl<'a, T: Config, I: 'static> + Transfer>> for Pallet +{ + fn transfer( + (collection, item): &Self::Id, + strategy: WithOrigin>, + ) -> DispatchResult { + let WithOrigin(origin, JustTo(dest)) = strategy; + + let signer = ensure_signed(origin)?; + + Self::do_transfer(collection.clone(), *item, dest.clone(), |collection_details, details| { + if details.owner != signer && collection_details.admin != signer { + let approved = details.approved.take().map_or(false, |i| i == signer); + ensure!(approved, Error::::NoPermission); + } + Ok(()) + }) + } +} + +impl<'a, T: Config, I: 'static> Transfer> for Pallet { + fn transfer((collection, item): &Self::Id, strategy: FromTo) -> DispatchResult { + let FromTo(from, to) = strategy; + + Self::do_transfer(collection.clone(), *item, to.clone(), |_, details| { + ensure!(details.owner == *from, Error::::WrongOwner); + Ok(()) + }) + } +} + +impl, I: 'static> Destroy for Pallet { + fn destroy((collection, item): &Self::Id, _strategy: JustDestroy) -> DispatchResult { + Self::do_burn(collection.clone(), *item, |_, _| Ok(())) + } +} + +impl<'a, T: Config, I: 'static> Destroy> + for Pallet +{ + fn destroy( + id @ (collection, item): &Self::Id, + strategy: WithOrigin, + ) -> DispatchResult { + let WithOrigin(origin, JustDestroy) = strategy; + let details = + Item::::get(collection, item).ok_or(Error::::UnknownCollection)?; + + >::destroy(id, WithOrigin(origin, IfOwnedBy(&details.owner))) + } +} + +impl<'a, T: Config, I: 'static> Destroy> for Pallet { + fn destroy((collection, item): &Self::Id, strategy: IfOwnedBy) -> DispatchResult { + let IfOwnedBy(who) = strategy; + + Self::do_burn(collection.clone(), *item, |_, d| { + ensure!(d.owner == *who, Error::::NoPermission); + Ok(()) + }) + } +} + +impl<'a, T: Config, I: 'static> + Destroy>> for Pallet +{ + fn destroy( + (collection, item): &Self::Id, + strategy: WithOrigin>, + ) -> DispatchResult { + let WithOrigin(origin, IfOwnedBy(who)) = strategy; + + let signer = ensure_signed(origin)?; + + Self::do_burn(collection.clone(), *item, |collection_details, details| { + let is_permitted = collection_details.admin == signer || details.owner == signer; + ensure!(is_permitted, Error::::NoPermission); + ensure!(*who == details.owner, Error::::WrongOwner); + Ok(()) + }) + } +} diff --git a/substrate/frame/uniques/src/impl_asset_ops/mod.rs b/substrate/frame/uniques/src/impl_asset_ops/mod.rs new file mode 100644 index 0000000000000..26143c510d873 --- /dev/null +++ b/substrate/frame/uniques/src/impl_asset_ops/mod.rs @@ -0,0 +1,2 @@ +mod class; +mod instance; diff --git a/substrate/frame/uniques/src/lib.rs b/substrate/frame/uniques/src/lib.rs index 2291d19de2bfb..bfd2c9ca59e87 100644 --- a/substrate/frame/uniques/src/lib.rs +++ b/substrate/frame/uniques/src/lib.rs @@ -36,6 +36,7 @@ pub mod mock; mod tests; mod functions; +mod impl_asset_ops; mod impl_nonfungibles; mod types; @@ -420,6 +421,14 @@ pub mod pallet { NotForSale, /// The provided bid is too low. BidTooLow, + /// No metadata is found. + NoMetadata, + /// Wrong metadata key/value bytes supplied. + WrongMetadata, + /// An attribute is not found. + AttributeNotFound, + /// Wrong attribute key/value bytes supplied. + WrongAttribute, } impl, I: 'static> Pallet { diff --git a/substrate/frame/uniques/src/types.rs b/substrate/frame/uniques/src/types.rs index a2e804f245f77..462bf20f6a30f 100644 --- a/substrate/frame/uniques/src/types.rs +++ b/substrate/frame/uniques/src/types.rs @@ -131,3 +131,7 @@ pub struct ItemMetadata> { /// Whether the item metadata may be changed by a non Force origin. pub(super) is_frozen: bool, } + +pub mod asset_strategies { + pub struct Attribute<'a>(pub &'a [u8]); +} From a2099c0a36d915576988abe2fb0bc8f24a0b5c82 Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Thu, 25 Apr 2024 18:50:38 +0200 Subject: [PATCH 03/41] feat(xcm): add unique instances adapters using asset ops --- .../xcm/xcm-builder/src/asset_conversion.rs | 24 ++- polkadot/xcm/xcm-builder/src/lib.rs | 5 +- .../src/unique_instances/backed_derivative.rs | 172 ++++++++++++++++++ .../xcm-builder/src/unique_instances/mod.rs | 35 ++++ .../src/unique_instances/recreateable.rs | 109 +++++++++++ .../src/unique_instances/transferable.rs | 108 +++++++++++ polkadot/xcm/xcm-executor/src/traits/mod.rs | 9 +- .../xcm-executor/src/traits/token_matching.rs | 17 +- 8 files changed, 472 insertions(+), 7 deletions(-) create mode 100644 polkadot/xcm/xcm-builder/src/unique_instances/backed_derivative.rs create mode 100644 polkadot/xcm/xcm-builder/src/unique_instances/mod.rs create mode 100644 polkadot/xcm/xcm-builder/src/unique_instances/recreateable.rs create mode 100644 polkadot/xcm/xcm-builder/src/unique_instances/transferable.rs diff --git a/polkadot/xcm/xcm-builder/src/asset_conversion.rs b/polkadot/xcm/xcm-builder/src/asset_conversion.rs index 520ce87448ea4..01e565dafa5c8 100644 --- a/polkadot/xcm/xcm-builder/src/asset_conversion.rs +++ b/polkadot/xcm/xcm-builder/src/asset_conversion.rs @@ -20,7 +20,9 @@ use frame_support::traits::{Contains, Get}; use sp_runtime::traits::MaybeEquivalence; use sp_std::{marker::PhantomData, prelude::*, result}; use xcm::latest::prelude::*; -use xcm_executor::traits::{Error as MatchError, MatchesFungibles, MatchesNonFungibles}; +use xcm_executor::traits::{ + Error as MatchError, MatchesFungibles, MatchesInstance, MatchesNonFungible, MatchesNonFungibles, +}; /// Converter struct implementing `AssetIdConversion` converting a numeric asset ID (must be /// `TryFrom/TryInto`) into a `GeneralIndex` junction, prefixed by some `Location` value. @@ -152,6 +154,26 @@ impl< } } +pub struct InstancesOfClasses(PhantomData); + +impl> + MatchesInstance<(ClassId, InstanceId)> for InstancesOfClasses +{ + fn matches_instance(a: &Asset) -> result::Result<(ClassId, InstanceId), MatchError> { + Matcher::matches_nonfungibles(a) + } +} + +pub struct ClasslessInstances(PhantomData); + +impl> MatchesInstance + for ClasslessInstances +{ + fn matches_instance(a: &Asset) -> result::Result { + Matcher::matches_nonfungible(a).ok_or(MatchError::AssetNotHandled) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/polkadot/xcm/xcm-builder/src/lib.rs b/polkadot/xcm/xcm-builder/src/lib.rs index cdc663a0cc9b4..40e7c2fd420c8 100644 --- a/polkadot/xcm/xcm-builder/src/lib.rs +++ b/polkadot/xcm/xcm-builder/src/lib.rs @@ -30,7 +30,8 @@ mod asset_conversion; #[allow(deprecated)] pub use asset_conversion::ConvertedConcreteAssetId; pub use asset_conversion::{ - AsPrefixedGeneralIndex, ConvertedConcreteId, MatchedConvertedConcreteId, + AsPrefixedGeneralIndex, ClasslessInstances, ConvertedConcreteId, InstancesOfClasses, + MatchedConvertedConcreteId, }; mod barriers; @@ -91,6 +92,8 @@ pub use matches_token::IsConcrete; mod matcher; pub use matcher::{CreateMatcher, MatchXcm, Matcher}; +pub mod unique_instances; + mod nonfungibles_adapter; pub use nonfungibles_adapter::{ NonFungiblesAdapter, NonFungiblesMutateAdapter, NonFungiblesTransferAdapter, diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/backed_derivative.rs b/polkadot/xcm/xcm-builder/src/unique_instances/backed_derivative.rs new file mode 100644 index 0000000000000..ba53262e16e6d --- /dev/null +++ b/polkadot/xcm/xcm-builder/src/unique_instances/backed_derivative.rs @@ -0,0 +1,172 @@ +use super::LOG_TARGET; +use core::marker::PhantomData; +use frame_support::traits::{ + tokens::asset_ops::{ + common_asset_kinds::{Class, Instance}, + common_strategies::{DeriveIdFrom, FromTo, Owned}, + AssetDefinition, Create, Transfer, + }, + Get, +}; +use xcm::latest::prelude::*; +use xcm_executor::traits::{ConvertLocation, Error as MatchError, MatchesInstance, TransactAsset}; + +/// The status of a derivative instance. +pub enum DerivativeStatus { + /// The derivative can be deposited (created) in the given class. + DepositableIn(ClassId), + + /// The derivative already exists and it has the given ID. + Exists(InstanceId), +} + +/// The `BackedDerivativeInstanceAdapter` implements the `TransactAsset` for unique instances +/// (NFT-like entities). +/// +/// The adapter uses the following asset operations: +/// * [`Create`] with the [`Owned`] strategy that uses the [`DeriveIdFrom`] id assignment. +/// * The [`DeriveIdFrom`] accepts the value of the `Id` type retrieved from the class [asset +/// definition](AssetDefinition) +/// (`ClassDef` generic parameter, represents a class-like entity such as a collection of NFTs). +/// * [`Transfer`] with [`FromTo`] strategy +/// +/// This adapter assumes that a new asset can be created and an existing asset can be transferred. +/// Also, the adapter assumes that the asset can't be destroyed. +/// So, it transfers the asset to the `StashLocation` on withdrawal. +/// +/// On deposit, the adapter consults the [`DerivativeStatus`] returned from the `Matcher`. +/// If the asset is depositable in a certain class, it will be created within that class. +/// Otherwise, if the asset exists, it will be transferred from the `StashLocation` to the +/// beneficiary. +/// +/// Transfers work as expected, transferring the asset from the `from` location to the beneficiary. +/// +/// This adapter is meant to be used in non-reserve locations where derivatives +/// can't be properly destroyed and then recreated. +/// +/// For instance, an NFT engine on the chain (a pallet or a smart contract) +/// can be incapable of recreating an NFT with the same ID. +/// So, we can only create a derivative with a new ID. +/// In this context, if we burn a derivative on withdrawal: +/// 1. we could exhaust the ID space for derivatives +/// 2. if "burning" means transferring to a special address (like the zero address in EVM), +/// we also waste the active storage space for burned derivatives +/// +/// To avoid that situation, the `StashLocation` is used to hold the withdrawn derivatives. +/// +/// Also, this adapter can be used in the NFT engine that simply doesn't support burning NFTs. +pub struct BackedDerivativeInstanceAdapter< + AccountId, + AccountIdConverter, + Matcher, + ClassDef, + InstanceOps, + StashLocation, +>(PhantomData<(AccountId, AccountIdConverter, Matcher, ClassDef, InstanceOps, StashLocation)>); + +impl TransactAsset + for BackedDerivativeInstanceAdapter< + AccountId, + AccountIdConverter, + Matcher, + ClassDef, + InstanceOps, + StashLocation, + > where + AccountIdConverter: ConvertLocation, + Matcher: MatchesInstance>, + ClassDef: AssetDefinition, + for<'a> InstanceOps: AssetDefinition + + Create, AccountId>> + + Transfer>, + StashLocation: Get, +{ + fn deposit_asset(what: &Asset, who: &Location, context: Option<&XcmContext>) -> XcmResult { + log::trace!( + target: LOG_TARGET, + "BackedDerivativeInstanceAdapter::deposit_asset what: {:?}, who: {:?}, context: {:?}", + what, + who, + context, + ); + + let derivative_status = Matcher::matches_instance(what)?; + let to = AccountIdConverter::convert_location(who) + .ok_or(MatchError::AccountIdConversionFailed)?; + + let result = match derivative_status { + DerivativeStatus::DepositableIn(class_id) => + InstanceOps::create(Owned::new(DeriveIdFrom::parent_id(&class_id), &to)) + .map(|_id| ()), + DerivativeStatus::Exists(instance_id) => { + let from = AccountIdConverter::convert_location(&StashLocation::get()) + .ok_or(MatchError::AccountIdConversionFailed)?; + + InstanceOps::transfer(&instance_id, FromTo(&from, &to)) + }, + }; + + result.map_err(|e| XcmError::FailedToTransactAsset(e.into())) + } + + fn withdraw_asset( + what: &Asset, + who: &Location, + maybe_context: Option<&XcmContext>, + ) -> Result { + log::trace!( + target: LOG_TARGET, + "BackedDerivativeInstanceAdapter::withdraw_asset what: {:?}, who: {:?}, context: {:?}", + what, + who, + maybe_context, + ); + + let derivative_status = Matcher::matches_instance(what)?; + let from = AccountIdConverter::convert_location(who) + .ok_or(MatchError::AccountIdConversionFailed)?; + + if let DerivativeStatus::Exists(instance_id) = derivative_status { + let to = AccountIdConverter::convert_location(&StashLocation::get()) + .ok_or(MatchError::AccountIdConversionFailed)?; + + InstanceOps::transfer(&instance_id, FromTo(&from, &to)) + .map_err(|e| XcmError::FailedToTransactAsset(e.into()))?; + + Ok(what.clone().into()) + } else { + Err(XcmError::NotWithdrawable) + } + } + + fn internal_transfer_asset( + what: &Asset, + from: &Location, + to: &Location, + context: &XcmContext, + ) -> Result { + log::trace!( + target: LOG_TARGET, + "BackedDerivativeInstanceAdapter::internal_transfer_asset what: {:?}, from: {:?}, to: {:?}, context: {:?}", + what, + from, + to, + context, + ); + + let derivative_status = Matcher::matches_instance(what)?; + let from = AccountIdConverter::convert_location(from) + .ok_or(MatchError::AccountIdConversionFailed)?; + let to = AccountIdConverter::convert_location(to) + .ok_or(MatchError::AccountIdConversionFailed)?; + + if let DerivativeStatus::Exists(instance_id) = derivative_status { + InstanceOps::transfer(&instance_id, FromTo(&from, &to)) + .map_err(|e| XcmError::FailedToTransactAsset(e.into()))?; + + Ok(what.clone().into()) + } else { + Err(XcmError::NotWithdrawable) + } + } +} diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/mod.rs b/polkadot/xcm/xcm-builder/src/unique_instances/mod.rs new file mode 100644 index 0000000000000..1134a04208add --- /dev/null +++ b/polkadot/xcm/xcm-builder/src/unique_instances/mod.rs @@ -0,0 +1,35 @@ +use frame_support::traits::tokens::asset_ops::{ + common_asset_kinds::Instance, common_strategies::FromTo, Transfer, +}; +use xcm::latest::prelude::*; +use xcm_executor::traits::{ConvertLocation, Error as MatchError, MatchesInstance}; + +const LOG_TARGET: &str = "xcm::unique_instances"; + +pub mod backed_derivative; +pub mod recreateable; +pub mod transferable; + +pub use backed_derivative::*; +pub use recreateable::*; +pub use transferable::*; + +fn transfer_instance< + AccountId, + AccountIdConverter: ConvertLocation, + Matcher: MatchesInstance, + InstanceTransfer: for<'a> Transfer>, +>( + what: &Asset, + from: &Location, + to: &Location, +) -> XcmResult { + let instance_id = Matcher::matches_instance(what)?; + let from = + AccountIdConverter::convert_location(from).ok_or(MatchError::AccountIdConversionFailed)?; + let to = + AccountIdConverter::convert_location(to).ok_or(MatchError::AccountIdConversionFailed)?; + + InstanceTransfer::transfer(&instance_id, FromTo(&from, &to)) + .map_err(|e| XcmError::FailedToTransactAsset(e.into())) +} diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/recreateable.rs b/polkadot/xcm/xcm-builder/src/unique_instances/recreateable.rs new file mode 100644 index 0000000000000..8426f7711eab8 --- /dev/null +++ b/polkadot/xcm/xcm-builder/src/unique_instances/recreateable.rs @@ -0,0 +1,109 @@ +use super::{transfer_instance, LOG_TARGET}; +use core::marker::PhantomData; +use frame_support::traits::tokens::asset_ops::{ + common_asset_kinds::Instance, + common_strategies::{FromTo, IfOwnedBy, Owned, PredefinedId}, + AssetDefinition, Create, Destroy, Transfer, +}; +use xcm::latest::prelude::*; +use xcm_executor::traits::{ConvertLocation, Error as MatchError, MatchesInstance, TransactAsset}; + +/// The `RecreateableInstanceAdapter` implements the `TransactAsset` for unique instances (NFT-like +/// entities). +/// The adapter uses the following asset operations: +/// * [`Create`] with the [`Owned`] strategy that uses the [`PredefinedId`]. +/// The `Id` used is the one from the [asset's definition](AssetDefinition). +/// * [`Transfer`] with [`FromTo`] strategy +/// * [`Destroy`] with [`IfOwnedBy`] strategy +/// +/// This adapter assumes that the asset can be safely destroyed +/// without the loss of any important data. It will destroy the asset on withdrawal. +/// Similarly, it assumes that the asset can be recreated with the same ID on deposit. +/// +/// Transfers work without additional assumptions. +/// +/// If the above assumptions are true, +/// this adapter can be used both to work with the original instances in a reserve location +/// and to work with derivatives in other locations. +/// +/// Only the assets matched by `Matcher` are affected. +/// If the `Matcher` recognizes the instance, it should return its `Id`. +/// +/// Note on teleports: +/// This adapter doesn't implement teleports at the moment since unique instances have associated +/// data that should be teleported along. +/// Currently, neither XCM has the ability to transfer such data +/// nor a standard approach exists in the ecosystem for this use case. +pub struct RecreateableInstanceAdapter( + PhantomData<(AccountId, AccountIdConverter, Matcher, InstanceOps)>, +); + +impl TransactAsset + for RecreateableInstanceAdapter +where + AccountIdConverter: ConvertLocation, + Matcher: MatchesInstance, + for<'a> InstanceOps: AssetDefinition + + Create, AccountId>> + + Transfer> + + Destroy>, +{ + fn deposit_asset(what: &Asset, who: &Location, context: Option<&XcmContext>) -> XcmResult { + log::trace!( + target: LOG_TARGET, + "RecreateableInstanceAdapter::deposit_asset what: {:?}, who: {:?}, context: {:?}", + what, + who, + context, + ); + + let instance_id = Matcher::matches_instance(what)?; + let who = AccountIdConverter::convert_location(who) + .ok_or(MatchError::AccountIdConversionFailed)?; + + InstanceOps::create(Owned::new(PredefinedId(&instance_id), &who)) + .map_err(|e| XcmError::FailedToTransactAsset(e.into())) + } + + fn withdraw_asset( + what: &Asset, + who: &Location, + maybe_context: Option<&XcmContext>, + ) -> Result { + log::trace!( + target: LOG_TARGET, + "RecreateableInstanceAdapter::withdraw_asset what: {:?}, who: {:?}, context: {:?}", + what, + who, + maybe_context, + ); + let instance_id = Matcher::matches_instance(what)?; + let who = AccountIdConverter::convert_location(who) + .ok_or(MatchError::AccountIdConversionFailed)?; + + InstanceOps::destroy(&instance_id, IfOwnedBy(&who)) + .map_err(|e| XcmError::FailedToTransactAsset(e.into()))?; + + Ok(what.clone().into()) + } + + fn internal_transfer_asset( + what: &Asset, + from: &Location, + to: &Location, + context: &XcmContext, + ) -> Result { + log::trace!( + target: LOG_TARGET, + "RecreateableInstanceAdapter::internal_transfer_asset what: {:?}, from: {:?}, to: {:?}, context: {:?}", + what, + from, + to, + context, + ); + + transfer_instance::(what, from, to)?; + + Ok(what.clone().into()) + } +} diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/transferable.rs b/polkadot/xcm/xcm-builder/src/unique_instances/transferable.rs new file mode 100644 index 0000000000000..2d2b0ced780e1 --- /dev/null +++ b/polkadot/xcm/xcm-builder/src/unique_instances/transferable.rs @@ -0,0 +1,108 @@ +use super::{transfer_instance, LOG_TARGET}; +use core::marker::PhantomData; +use frame_support::traits::{ + tokens::asset_ops::{common_asset_kinds::Instance, common_strategies::FromTo, Transfer}, + Get, +}; +use xcm::latest::prelude::*; +use xcm_executor::traits::{ConvertLocation, MatchesInstance, TransactAsset}; + +/// The `TransferableInstanceAdapter` implements the `TransactAsset` for unique instances (NFT-like +/// entities). +/// +/// The adapter uses only the [`Transfer`] asset operation with the [`FromTo`] strategy. +/// +/// It is meant to be used when the asset can't be safely destroyed on withdrawal +/// (i.e., the absence of the loss of important data can't be guaranteed when the asset is +/// destroyed). Equivalently, this adapter may be used when the asset can't be recreated on deposit. +/// +/// The adapter uses the `StashLocation` as the beneficiary to transfer the asset on withdrawal. +/// On deposit, the asset will be transferred from the `StashLocation` to the beneficiary. +/// +/// Transfers work as expected, transferring the asset from the `from` location to the beneficiary. +/// +/// This adapter can be used only in a reserve location. +/// It can't create new instances, hence it can't create derivatives. +pub struct TransferableInstanceAdapter< + AccountId, + AccountIdConverter, + Matcher, + InstanceTransfer, + StashLocation, +>(PhantomData<(AccountId, AccountIdConverter, Matcher, InstanceTransfer, StashLocation)>); + +impl< + AccountId, + AccountIdConverter: ConvertLocation, + Matcher: MatchesInstance, + InstanceTransfer: for<'a> Transfer>, + StashLocation: Get, + > TransactAsset + for TransferableInstanceAdapter< + AccountId, + AccountIdConverter, + Matcher, + InstanceTransfer, + StashLocation, + > +{ + fn deposit_asset(what: &Asset, who: &Location, context: Option<&XcmContext>) -> XcmResult { + log::trace!( + target: LOG_TARGET, + "TransferableInstanceAdapter::deposit_asset what: {:?}, who: {:?}, context: {:?}", + what, + who, + context, + ); + + transfer_instance::( + what, + &StashLocation::get(), + who, + ) + } + + fn withdraw_asset( + what: &Asset, + who: &Location, + maybe_context: Option<&XcmContext>, + ) -> Result { + log::trace!( + target: LOG_TARGET, + "TransferableInstanceAdapter::withdraw_asset what: {:?}, who: {:?}, context: {:?}", + what, + who, + maybe_context, + ); + + transfer_instance::( + what, + who, + &StashLocation::get(), + )?; + + Ok(what.clone().into()) + } + + fn internal_transfer_asset( + what: &Asset, + from: &Location, + to: &Location, + context: &XcmContext, + ) -> Result { + log::trace!( + target: LOG_TARGET, + "TransferableInstanceAdapter::internal_transfer_asset what: {:?}, from: {:?}, to: {:?}, context: {:?}", + what, + from, + to, + context, + ); + + transfer_instance::( + what, from, to, + )?; + + Ok(what.clone().into()) + } +} diff --git a/polkadot/xcm/xcm-executor/src/traits/mod.rs b/polkadot/xcm/xcm-executor/src/traits/mod.rs index aa3f0d26e3025..7f08c724e8d87 100644 --- a/polkadot/xcm/xcm-executor/src/traits/mod.rs +++ b/polkadot/xcm/xcm-executor/src/traits/mod.rs @@ -35,7 +35,8 @@ mod filter_asset_location; pub use filter_asset_location::FilterAssetLocation; mod token_matching; pub use token_matching::{ - Error, MatchesFungible, MatchesFungibles, MatchesNonFungible, MatchesNonFungibles, + Error, MatchesFungible, MatchesFungibles, MatchesInstance, MatchesNonFungible, + MatchesNonFungibles, }; mod on_response; pub use on_response::{OnResponse, QueryHandler, QueryResponseStatus, VersionChangeNotifier}; @@ -58,9 +59,9 @@ pub mod prelude { pub use super::{ export_xcm, validate_export, AssetExchange, AssetLock, ClaimAssets, ConvertOrigin, DropAssets, Enact, Error, ExportXcm, FeeManager, FeeReason, LockError, MatchesFungible, - MatchesFungibles, MatchesNonFungible, MatchesNonFungibles, OnResponse, ProcessTransaction, - ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, WeightTrader, - WithOriginFilter, + MatchesFungibles, MatchesInstance, MatchesNonFungible, MatchesNonFungibles, OnResponse, + ProcessTransaction, ShouldExecute, TransactAsset, VersionChangeNotifier, WeightBounds, + WeightTrader, WithOriginFilter, }; #[allow(deprecated)] pub use super::{Identity, JustTry}; diff --git a/polkadot/xcm/xcm-executor/src/traits/token_matching.rs b/polkadot/xcm/xcm-executor/src/traits/token_matching.rs index e9a7e3ad845da..d62e624063450 100644 --- a/polkadot/xcm/xcm-executor/src/traits/token_matching.rs +++ b/polkadot/xcm/xcm-executor/src/traits/token_matching.rs @@ -101,7 +101,22 @@ impl MatchesNonFungibles for Tuple { for_tuples!( #( match Tuple::matches_nonfungibles(a) { o @ Ok(_) => return o, _ => () } )* ); - log::trace!(target: "xcm::matches_non_fungibles", "did not match fungibles asset: {:?}", &a); + log::trace!(target: "xcm::matches_non_fungibles", "did not match non-fungibles asset: {:?}", &a); + Err(Error::AssetNotHandled) + } +} + +pub trait MatchesInstance { + fn matches_instance(a: &Asset) -> result::Result; +} + +#[impl_trait_for_tuples::impl_for_tuples(30)] +impl MatchesInstance for Tuple { + fn matches_instance(a: &Asset) -> result::Result { + for_tuples!( #( + match Tuple::matches_instance(a) { o @ Ok(_) => return o, _ => () } + )* ); + log::trace!(target: "xcm::matches_instance", "did not match an asset instance: {:?}", &a); Err(Error::AssetNotHandled) } } From 3f7c3a2217c8d324a39e517d5fb18bba62bad071 Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Thu, 25 Apr 2024 19:14:58 +0200 Subject: [PATCH 04/41] feat(rococo,westend): use xcm unique instances adapter --- .../assets/asset-hub-rococo/src/xcm_config.rs | 25 ++++++----------- .../asset-hub-westend/src/xcm_config.rs | 28 +++++++------------ 2 files changed, 19 insertions(+), 34 deletions(-) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs index dbf27fb39ac51..6c5f9f1f630fb 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs @@ -49,11 +49,12 @@ use testnet_parachains_constants::rococo::snowbridge::{ }; use xcm::latest::prelude::*; use xcm_builder::{ - AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, - AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, - DenyThenTry, DescribeAllTerminal, DescribeFamily, EnsureXcmOrigin, FrameTransactionalProcessor, + unique_instances::RecreateableInstanceAdapter, AccountId32Aliases, + AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, + AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, DenyThenTry, + DescribeAllTerminal, DescribeFamily, EnsureXcmOrigin, FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, GlobalConsensusParachainConvertsFor, HashedDescription, - IsConcrete, LocalMint, NetworkExportTableItem, NoChecking, NonFungiblesAdapter, + InstancesOfClasses, IsConcrete, LocalMint, NetworkExportTableItem, NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignPaidRemoteExporter, SovereignSignedViaLocation, StartsWith, @@ -149,19 +150,11 @@ pub type UniquesConvertedConcreteId = assets_common::UniquesConvertedConcreteId; /// Means for transacting unique assets. -pub type UniquesTransactor = NonFungiblesAdapter< - // Use this non-fungibles implementation: - Uniques, - // This adapter will handle any non-fungible asset from the uniques pallet. - UniquesConvertedConcreteId, - // Convert an XCM Location into a local account id: - LocationToAccountId, - // Our chain's account ID type (we can't get away without mentioning it explicitly): +pub type UniquesTransactor = RecreateableInstanceAdapter< AccountId, - // Does not check teleports. - NoChecking, - // The account to use for tracking teleports. - CheckingAccount, + LocationToAccountId, + InstancesOfClasses, + Uniques, >; /// `AssetId`/`Balance` converter for `ForeignAssets`. diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index ed8a58af396c7..9c062902fa41e 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -45,12 +45,12 @@ use polkadot_runtime_common::xcm_sender::ExponentialPrice; use sp_runtime::traits::{AccountIdConversion, ConvertInto}; use xcm::latest::prelude::*; use xcm_builder::{ - AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, - AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, - DenyThenTry, DescribeFamily, DescribePalletTerminal, EnsureXcmOrigin, - FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, - GlobalConsensusParachainConvertsFor, HashedDescription, IsConcrete, LocalMint, - NetworkExportTableItem, NoChecking, NonFungiblesAdapter, ParentAsSuperuser, ParentIsPreset, + unique_instances::RecreateableInstanceAdapter, AccountId32Aliases, + AllowExplicitUnpaidExecutionFrom, AllowKnownQueryResponses, AllowSubscriptionsFrom, + AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, DenyThenTry, DescribeFamily, + DescribePalletTerminal, EnsureXcmOrigin, FrameTransactionalProcessor, FungibleAdapter, + FungiblesAdapter, GlobalConsensusParachainConvertsFor, HashedDescription, InstancesOfClasses, + IsConcrete, LocalMint, NetworkExportTableItem, NoChecking, ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, @@ -142,19 +142,11 @@ pub type UniquesConvertedConcreteId = assets_common::UniquesConvertedConcreteId; /// Means for transacting unique assets. -pub type UniquesTransactor = NonFungiblesAdapter< - // Use this non-fungibles implementation: - Uniques, - // This adapter will handle any non-fungible asset from the uniques pallet. - UniquesConvertedConcreteId, - // Convert an XCM Location into a local account id: - LocationToAccountId, - // Our chain's account ID type (we can't get away without mentioning it explicitly): +pub type UniquesTransactor = RecreateableInstanceAdapter< AccountId, - // Does not check teleports. - NoChecking, - // The account to use for tracking teleports. - CheckingAccount, + LocationToAccountId, + InstancesOfClasses, + Uniques, >; /// `AssetId`/`Balance` converter for `ForeignAssets`. From 0016ec271cc5fa97edd2d3dbdd9e535d26308cda Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Tue, 11 Jun 2024 19:02:29 +0200 Subject: [PATCH 05/41] refactor: asset-ops related stuff, add Stash+Restore ops --- .../support/src/traits/tokens/asset_ops.rs | 201 +++++++++++------- .../frame/uniques/src/impl_asset_ops/class.rs | 22 +- .../uniques/src/impl_asset_ops/instance.rs | 41 ++-- 3 files changed, 156 insertions(+), 108 deletions(-) diff --git a/substrate/frame/support/src/traits/tokens/asset_ops.rs b/substrate/frame/support/src/traits/tokens/asset_ops.rs index f44ec23366996..819e7933352d5 100644 --- a/substrate/frame/support/src/traits/tokens/asset_ops.rs +++ b/substrate/frame/support/src/traits/tokens/asset_ops.rs @@ -58,8 +58,8 @@ pub trait MetadataInspectStrategy { /// A trait representing the ability of a certain asset kind to **provide** its metadata /// information. /// -/// This trait can be implemented multiple times using different [`inspect -/// strategies`](MetadataInspectStrategy). +/// This trait can be implemented multiple times using different +/// [`inspect strategies`](MetadataInspectStrategy). /// /// An inspect strategy defines how the asset metadata is identified/retrieved /// and what [`Value`](MetadataInspectStrategy::Value) type is returned. @@ -91,8 +91,8 @@ pub trait MetadataUpdateStrategy { /// A trait representing the ability of a certain asset kind to **update** its metadata information. /// -/// This trait can be implemented multiple times using different [`update -/// strategies`](MetadataUpdateStrategy). +/// This trait can be implemented multiple times using different +/// [`update strategies`](MetadataUpdateStrategy). /// /// An update strategy defines how the asset metadata is identified /// and what [`Update`](MetadataUpdateStrategy::Update) type is used. @@ -125,23 +125,22 @@ pub trait CreateStrategy { /// /// The common ID assignments are: /// * [`AutoId`](common_strategies::AutoId) -/// * [`PredefinedId`](common_strategies::PredefinedId) -/// * [`DeriveIdFrom`](common_strategies::DeriveIdFrom) +/// * [`AssignId`](common_strategies::AssignId) +/// * [`DeriveAndReportId`](common_strategies::DeriveAndReportId) pub trait IdAssignment { /// The reported ID type. /// /// Examples: /// * [`AutoId`](common_strategies::AutoId) returns ID of the newly created asset - /// * [`PredefinedId`](common_strategies::PredefinedId) returns `()` since the ID is already - /// defined - /// * [`DeriveIdFrom`](common_strategies::DeriveIdFrom) returns the derived ID + /// * [`AssignId`](common_strategies::AssignId) doesn't report an ID, i.e., returns `()` + /// * [`DeriveAndReportId`](common_strategies::DeriveAndReportId) returns the derived ID type ReportedId; } /// A trait representing the ability of a certain asset kind to be created. /// -/// This trait can be implemented multiple times using different [`"create" -/// strategies`](CreateStrategy). +/// This trait can be implemented multiple times using different +/// [`"create" strategies`](CreateStrategy). /// /// A create strategy defines all aspects of asset creation including how an asset ID is assigned. pub trait Create { @@ -152,14 +151,14 @@ pub trait Create { /// A strategy for use in the [`Transfer`] implementations. /// /// The common transfer strategies are: -/// * [`JustTo`](common_strategies::JustTo) +/// * [`JustDo`](common_strategies::JustDo) /// * [`FromTo`](common_strategies::FromTo) pub trait TransferStrategy {} /// A trait representing the ability of a certain asset kind to be transferred. /// -/// This trait can be implemented multiple times using different [`transfer -/// strategies`](TransferStrategy). +/// This trait can be implemented multiple times using different +/// [`transfer strategies`](TransferStrategy). /// /// A transfer strategy defines transfer parameters. pub trait Transfer: AssetDefinition { @@ -172,7 +171,7 @@ pub trait Transfer: AssetDefinition: AssetDefinition { @@ -195,6 +194,45 @@ pub trait Destroy: AssetDefinition Result; } +/// A strategy for use in the [`Stash`] implementations. +/// +/// The common stash strategies are: +/// * [`JustDo`](common_strategies::JustDo) +/// * [`IfOwnedBy`](common_strategies::IfOwnedBy) +pub trait StashStrategy {} + +/// A trait representing the ability of a certain asset kind to be stashed. +/// +/// This trait can be implemented multiple times using different +/// [`stash strategies`](StashStrategy). +/// +/// A stash strategy defines stash parameters. +pub trait Stash: AssetDefinition { + /// Stash the asset identified by the given `id` using the provided `strategy`. + /// + /// The ID type is retrieved from the [`AssetDefinition`]. + fn stash(id: &Self::Id, strategy: Strategy) -> DispatchResult; +} + +/// A strategy for use in the [`Restore`] implementations. +/// The common stash strategies are: +/// * [`JustDo`](common_strategies::JustDo) +/// * [`IfRestorable`](common_strategies::IfRestorable) +pub trait RestoreStrategy {} + +/// A trait representing the ability of a certain asset kind to be restored. +/// +/// This trait can be implemented multiple times using different +/// [`restore strategies`](RestoreStrategy). +/// +/// A restore strategy defines restore parameters. +pub trait Restore: AssetDefinition { + /// Restore the asset identified by the given `id` using the provided `strategy`. + /// + /// The ID type is retrieved from the [`AssetDefinition`]. + fn restore(id: &Self::Id, strategy: Strategy) -> DispatchResult; +} + /// This modules contains the common asset kinds. pub mod common_asset_kinds { /// The `Class` asset kind is of assets that resemble class-like entities. @@ -238,6 +276,31 @@ pub mod common_strategies { type Success = Inner::Success; } + /// The JustDo represents the simplest strategy, + /// which doesn't require additional checks to perform the operation. + /// + /// It can be used as the following strategies: + /// * [`transfer strategy`](TransferStrategy) + /// * [`destroy strategy`](DestroyStrategy) + /// * [`stash strategy`](StashStrategy) + /// * [`restore strategy`](RestoreStrategy) + /// + /// It accepts whatever parameters are set in its generic argument. + /// For instance, for an unchecked transfer, + /// this strategy may take a reference to a beneficiary account. + pub struct JustDo<'a, Params = ()>(pub &'a Params); + impl<'a> Default for JustDo<'a, ()> { + fn default() -> Self { + Self(&()) + } + } + impl<'a, Owner> TransferStrategy for JustDo<'a, Owner> {} + impl<'a> DestroyStrategy for JustDo<'a> { + type Success = (); + } + impl<'a> StashStrategy for JustDo<'a> {} + impl<'a, Owner> RestoreStrategy for JustDo<'a, Owner> {} + /// The `Bytes` strategy represents raw metadata bytes. /// It is both an [inspect](MetadataInspectStrategy) and [update](MetadataUpdateStrategy) /// metadata strategy. @@ -371,8 +434,8 @@ pub mod common_strategies { type Update<'u> = bool; } - /// The `AutoId` is an ID assignment approach intended to be used in [`"create" - /// strategies`](CreateStrategy). + /// The `AutoId` is an ID assignment approach intended to be used in + /// [`"create" strategies`](CreateStrategy). /// /// It accepts the `Id` type of the asset. /// The "create" strategy should report the value of type `Id` upon successful asset creation. @@ -386,68 +449,68 @@ pub mod common_strategies { type ReportedId = Id; } - /// The `PredefinedId` is an ID assignment approach intended to be used in [`"create" - /// strategies`](CreateStrategy). + /// The `AssignId` is an ID assignment approach intended to be used in + /// [`"create" strategies`](CreateStrategy). /// - /// It accepts a value of the `Id` type. - /// The "create" strategy should use the provided ID value to create a new asset. - pub struct PredefinedId<'a, Id>(pub &'a Id); - impl<'a, Id> IdAssignment for PredefinedId<'a, Id> { + /// It accepts `Params` to assign an ID to the newly created asset. + /// This ID assignment approach doesn't report the ID upon the asset's creation. + pub struct AssignId<'a, Params>(pub &'a Params); + impl<'a, Params> IdAssignment for AssignId<'a, Params> { type ReportedId = (); } - /// The `DeriveIdFrom` is an ID assignment approach intended to be used in [`"create" - /// strategies`](CreateStrategy). + /// The `DeriveAndReportId` is an ID assignment approach intended to be used in + /// [`"create" strategies`](CreateStrategy). /// - /// It accepts the `ParentId` and the `ChildId`. - /// The `ChildId` value should be computed by the "create" strategy using the `ParentId` value. + /// It accepts the `Params` and the `Id`. + /// The `Id` value should be computed by the "create" strategy using the `Params` value. /// - /// The "create" strategy should report the `ChildId` value upon successful asset creation. + /// The "create" strategy should report the `Id` value upon successful asset creation. /// /// An example of ID derivation is the creation of an NFT inside a collection using the - /// collection ID. The child ID in this case is the full ID of the NFT. - pub struct DeriveIdFrom<'a, ParentId, ChildId>(pub &'a ParentId, PhantomData); - impl<'a, ParentId, ChildId> DeriveIdFrom<'a, ParentId, ChildId> { - pub fn parent_id(primary_id: &'a ParentId) -> Self { - Self(primary_id, PhantomData) + /// collection ID as `Params`. The `Id` in this case is the full ID of the NFT. + pub struct DeriveAndReportId<'a, Params, Id>(pub &'a Params, pub PhantomData); + impl<'a, Params, Id> DeriveAndReportId<'a, Params, Id> { + pub fn from(params: &'a Params) -> Self { + Self(params, PhantomData) } } - impl<'a, ParentId, ChildId> IdAssignment for DeriveIdFrom<'a, ParentId, ChildId> { - type ReportedId = ChildId; + impl<'a, ParentId, Id> IdAssignment for DeriveAndReportId<'a, ParentId, Id> { + type ReportedId = Id; } /// The `Owned` is a [`"create" strategy`](CreateStrategy). /// /// It accepts: - /// * The [ID assignment](IdAssignment) approach /// * The `owner` + /// * The [ID assignment](IdAssignment) approach /// * The optional `config` /// * The optional creation `witness` /// /// The [`Success`](CreateStrategy::Success) will contain /// the [reported ID](IdAssignment::ReportedId) of the ID assignment approach. - pub struct Owned<'a, Assignment: IdAssignment, Owner, Config = (), Witness = ()> { - pub id_assignment: Assignment, + pub struct Owned<'a, Owner, Assignment: IdAssignment, Config = (), Witness = ()> { pub owner: &'a Owner, + pub id_assignment: Assignment, pub config: &'a Config, pub witness: &'a Witness, } - impl<'a, Assignment: IdAssignment, Owner> Owned<'a, Assignment, Owner, (), ()> { - pub fn new(id_assignment: Assignment, owner: &'a Owner) -> Self { + impl<'a, Owner, Assignment: IdAssignment> Owned<'a, Owner, Assignment, (), ()> { + pub fn new(owner: &'a Owner, id_assignment: Assignment) -> Self { Self { id_assignment, owner, config: &(), witness: &() } } } - impl<'a, Assignment: IdAssignment, Owner, Config> Owned<'a, Assignment, Owner, Config, ()> { + impl<'a, Owner, Assignment: IdAssignment, Config> Owned<'a, Owner, Assignment, Config, ()> { pub fn new_configured( - id_assignment: Assignment, owner: &'a Owner, + id_assignment: Assignment, config: &'a Config, ) -> Self { Self { id_assignment, owner, config, witness: &() } } } - impl<'a, Assignment: IdAssignment, Owner, Config, Witness> CreateStrategy - for Owned<'a, Assignment, Owner, Config, Witness> + impl<'a, Owner, Assignment: IdAssignment, Config, Witness> CreateStrategy + for Owned<'a, Owner, Assignment, Config, Witness> { type Success = Assignment::ReportedId; } @@ -455,71 +518,67 @@ pub mod common_strategies { /// The `Adminable` is a [`"create" strategy`](CreateStrategy). /// /// It accepts: - /// * The [ID assignment](IdAssignment) approach /// * The `owner` /// * The `admin` + /// * The [ID assignment](IdAssignment) approach /// * The optional `config` /// * The optional creation `witness` /// /// The [`Success`](CreateStrategy::Success) will contain /// the [reported ID](IdAssignment::ReportedId) of the ID assignment approach. - pub struct Adminable<'a, Assignment: IdAssignment, Account, Config = (), Witness = ()> { - pub id_assignment: Assignment, + pub struct Adminable<'a, Account, Assignment: IdAssignment, Config = (), Witness = ()> { pub owner: &'a Account, pub admin: &'a Account, + pub id_assignment: Assignment, pub config: &'a Config, pub witness: &'a Witness, } - impl<'a, Assignment: IdAssignment, Account> Adminable<'a, Assignment, Account, (), ()> { + impl<'a, Account, Assignment: IdAssignment> Adminable<'a, Account, Assignment, (), ()> { pub fn new(id_assignment: Assignment, owner: &'a Account, admin: &'a Account) -> Self { Self { id_assignment, owner, admin, config: &(), witness: &() } } } - impl<'a, Assignment: IdAssignment, Account, Config> Adminable<'a, Assignment, Account, Config, ()> { + impl<'a, Account, Assignment: IdAssignment, Config> Adminable<'a, Account, Assignment, Config, ()> { pub fn new_configured( - id_assignment: Assignment, owner: &'a Account, admin: &'a Account, + id_assignment: Assignment, config: &'a Config, ) -> Self { Self { id_assignment, owner, admin, config, witness: &() } } } - impl<'a, Assignment: IdAssignment, Account, Config, Witness> CreateStrategy - for Adminable<'a, Assignment, Account, Config, Witness> + impl<'a, Account, Assignment: IdAssignment, Config, Witness> CreateStrategy + for Adminable<'a, Account, Assignment, Config, Witness> { type Success = Assignment::ReportedId; } - /// The `JustTo` is a [`transfer strategy`](TransferStrategy). - /// - /// It accepts the target of the transfer, - /// i.e., who will become the asset's owner after the transfer. - pub struct JustTo<'a, Owner>(pub &'a Owner); - impl<'a, Owner> TransferStrategy for JustTo<'a, Owner> {} - /// The `FromTo` is a [`transfer strategy`](TransferStrategy). /// /// It accepts two parameters: `from` and `to` whom the asset should be transferred. pub struct FromTo<'a, Owner>(pub &'a Owner, pub &'a Owner); impl<'a, Owner> TransferStrategy for FromTo<'a, Owner> {} - /// The `JustDestroy` is a [`destroy strategy`](DestroyStrategy). - /// - /// It represents an "unchecked" destruction of the asset. - pub struct JustDestroy; - impl DestroyStrategy for JustDestroy { - type Success = (); - } - - /// The `IfOwnedBy` is a [`destroy strategy`](DestroyStrategy). + /// The `IfOwnedBy` is both a [`destroy strategy`](DestroyStrategy) + /// and a [`stash strategy`](StashStrategy). /// /// It accepts a possible owner of the asset. - /// If the provided entity owns the asset, it will be destroyed. + /// If the provided entity owns the asset, the corresponding operation will be performed. pub struct IfOwnedBy<'a, Owner>(pub &'a Owner); impl<'a, Owner> DestroyStrategy for IfOwnedBy<'a, Owner> { type Success = (); } + impl<'a, Owner> StashStrategy for IfOwnedBy<'a, Owner> {} + + /// The `IfRestorable` is a [`restore strategy`](RestoreStrategy). + /// + /// It accepts whatever parameters are set in its generic argument. + /// For instance, if an asset is restorable, + /// this strategy may reference a beneficiary account, + /// which should own the asset upon restoration. + pub struct IfRestorable<'a, Params>(pub &'a Params); + impl<'a, Params> RestoreStrategy for IfRestorable<'a, Params> {} /// The `WithWitness` is a [`destroy strategy`](DestroyStrategy). /// diff --git a/substrate/frame/uniques/src/impl_asset_ops/class.rs b/substrate/frame/uniques/src/impl_asset_ops/class.rs index 9e64dc4a2ed61..f3efbbd79f97a 100644 --- a/substrate/frame/uniques/src/impl_asset_ops/class.rs +++ b/substrate/frame/uniques/src/impl_asset_ops/class.rs @@ -6,7 +6,7 @@ use frame_support::{ tokens::asset_ops::{ common_asset_kinds::Class, common_strategies::{ - Adminable, Bytes, IfOwnedByWithWitness, Ownership, PredefinedId, WithOrigin, + Adminable, AssignId, Bytes, IfOwnedByWithWitness, Ownership, WithOrigin, WithWitness, }, AssetDefinition, Create, Destroy, InspectMetadata, @@ -57,12 +57,10 @@ impl<'a, T: Config, I: 'static> InspectMetadata>> } impl<'a, T: Config, I: 'static> - Create, T::AccountId>> for Pallet + Create>> for Pallet { - fn create( - strategy: Adminable, T::AccountId>, - ) -> DispatchResult { - let Adminable { id_assignment: PredefinedId(collection), owner, admin, .. } = strategy; + fn create(strategy: Adminable>) -> DispatchResult { + let Adminable { owner, admin, id_assignment: AssignId(collection), .. } = strategy; Self::do_create_collection( collection.clone(), @@ -82,21 +80,15 @@ impl<'a, T: Config, I: 'static> impl<'a, T: Config, I: 'static> Create< Class, - WithOrigin< - T::RuntimeOrigin, - Adminable<'a, PredefinedId<'a, T::CollectionId>, T::AccountId>, - >, + WithOrigin>>, > for Pallet { fn create( - strategy: WithOrigin< - T::RuntimeOrigin, - Adminable, T::AccountId>, - >, + strategy: WithOrigin>>, ) -> DispatchResult { let WithOrigin( origin, - creation @ Adminable { id_assignment: PredefinedId(collection), owner, .. }, + creation @ Adminable { owner, id_assignment: AssignId(collection), .. }, ) = strategy; let maybe_check_signer = diff --git a/substrate/frame/uniques/src/impl_asset_ops/instance.rs b/substrate/frame/uniques/src/impl_asset_ops/instance.rs index ad55a4efc662d..ac4cb2443df9d 100644 --- a/substrate/frame/uniques/src/impl_asset_ops/instance.rs +++ b/substrate/frame/uniques/src/impl_asset_ops/instance.rs @@ -5,8 +5,7 @@ use frame_support::{ traits::tokens::asset_ops::{ common_asset_kinds::Instance, common_strategies::{ - Bytes, CanTransfer, FromTo, IfOwnedBy, JustDestroy, JustTo, Owned, Ownership, - PredefinedId, WithOrigin, + AssignId, Bytes, CanTransfer, FromTo, IfOwnedBy, JustDo, Owned, Ownership, WithOrigin, }, AssetDefinition, Create, Destroy, InspectMetadata, Transfer, }, @@ -71,13 +70,13 @@ impl, I: 'static> InspectMetadata for Pallet } impl<'a, T: Config, I: 'static> - Create, T::AccountId>> + Create>> for Pallet { fn create( - strategy: Owned, T::AccountId>, + strategy: Owned>, ) -> DispatchResult { - let Owned { id_assignment: PredefinedId((collection, item)), owner, .. } = strategy; + let Owned { owner, id_assignment: AssignId((collection, item)), .. } = strategy; Self::do_mint(collection.clone(), *item, owner.clone(), |_| Ok(())) } @@ -88,20 +87,18 @@ impl<'a, T: Config, I: 'static> Instance, WithOrigin< T::RuntimeOrigin, - Owned<'a, PredefinedId<'a, (T::CollectionId, T::ItemId)>, T::AccountId>, + Owned<'a, T::AccountId, AssignId<'a, (T::CollectionId, T::ItemId)>>, >, > for Pallet { fn create( strategy: WithOrigin< T::RuntimeOrigin, - Owned, T::AccountId>, + Owned>, >, ) -> DispatchResult { - let WithOrigin( - origin, - Owned { id_assignment: PredefinedId((collection, item)), owner, .. }, - ) = strategy; + let WithOrigin(origin, Owned { owner, id_assignment: AssignId((collection, item)), .. }) = + strategy; let signer = ensure_signed(origin)?; @@ -112,22 +109,22 @@ impl<'a, T: Config, I: 'static> } } -impl<'a, T: Config, I: 'static> Transfer> for Pallet { - fn transfer((collection, item): &Self::Id, strategy: JustTo) -> DispatchResult { - let JustTo(dest) = strategy; +impl<'a, T: Config, I: 'static> Transfer> for Pallet { + fn transfer((collection, item): &Self::Id, strategy: JustDo) -> DispatchResult { + let JustDo(dest) = strategy; Self::do_transfer(collection.clone(), *item, dest.clone(), |_, _| Ok(())) } } impl<'a, T: Config, I: 'static> - Transfer>> for Pallet + Transfer>> for Pallet { fn transfer( (collection, item): &Self::Id, - strategy: WithOrigin>, + strategy: WithOrigin>, ) -> DispatchResult { - let WithOrigin(origin, JustTo(dest)) = strategy; + let WithOrigin(origin, JustDo(dest)) = strategy; let signer = ensure_signed(origin)?; @@ -152,20 +149,20 @@ impl<'a, T: Config, I: 'static> Transfer> } } -impl, I: 'static> Destroy for Pallet { - fn destroy((collection, item): &Self::Id, _strategy: JustDestroy) -> DispatchResult { +impl, I: 'static> Destroy> for Pallet { + fn destroy((collection, item): &Self::Id, _strategy: JustDo) -> DispatchResult { Self::do_burn(collection.clone(), *item, |_, _| Ok(())) } } -impl<'a, T: Config, I: 'static> Destroy> +impl<'a, T: Config, I: 'static> Destroy>> for Pallet { fn destroy( id @ (collection, item): &Self::Id, - strategy: WithOrigin, + strategy: WithOrigin, ) -> DispatchResult { - let WithOrigin(origin, JustDestroy) = strategy; + let WithOrigin(origin, _just_do) = strategy; let details = Item::::get(collection, item).ok_or(Error::::UnknownCollection)?; From 17f54ab5aacb98e5b3be48b1c1bd7982ea73912a Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Tue, 11 Jun 2024 19:39:44 +0200 Subject: [PATCH 06/41] refactor: unique instances XCM stuff --- .../assets/asset-hub-rococo/src/xcm_config.rs | 28 +-- .../asset-hub-westend/src/xcm_config.rs | 19 +- .../xcm/xcm-builder/src/asset_conversion.rs | 8 +- polkadot/xcm/xcm-builder/src/lib.rs | 2 +- .../src/unique_instances/adapter.rs | 155 ++++++++++++++++ .../src/unique_instances/backed_derivative.rs | 172 ------------------ .../src/unique_instances/derivatives.rs | 164 +++++++++++++++++ .../xcm-builder/src/unique_instances/mod.rs | 39 +--- .../xcm-builder/src/unique_instances/ops.rs | 120 ++++++++++++ .../src/unique_instances/recreateable.rs | 109 ----------- .../src/unique_instances/transferable.rs | 108 ----------- 11 files changed, 476 insertions(+), 448 deletions(-) create mode 100644 polkadot/xcm/xcm-builder/src/unique_instances/adapter.rs delete mode 100644 polkadot/xcm/xcm-builder/src/unique_instances/backed_derivative.rs create mode 100644 polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs create mode 100644 polkadot/xcm/xcm-builder/src/unique_instances/ops.rs delete mode 100644 polkadot/xcm/xcm-builder/src/unique_instances/recreateable.rs delete mode 100644 polkadot/xcm/xcm-builder/src/unique_instances/transferable.rs diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs index 84ff35c966efb..325be06e71c45 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs @@ -49,18 +49,18 @@ use testnet_parachains_constants::rococo::snowbridge::{ }; use xcm::latest::prelude::*; use xcm_builder::{ - unique_instances::RecreateableInstanceAdapter, AccountId32Aliases, - AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, - AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, - DenyReserveTransferToRelayChain, DenyThenTry, DescribeAllTerminal, DescribeFamily, - EnsureXcmOrigin, FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, - GlobalConsensusParachainConvertsFor, HashedDescription, InstancesOfClasses, IsConcrete, - LocalMint, NetworkExportTableItem, NoChecking, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignPaidRemoteExporter, - SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, TakeWeightCredit, - TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, - XcmFeeManagerFromComponents, XcmFeeToAccount, + unique_instances::UniqueInstancesAdapter, AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, + AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, + AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, DenyThenTry, + DescribeAllTerminal, DescribeFamily, EnsureXcmOrigin, FrameTransactionalProcessor, + FungibleAdapter, FungiblesAdapter, GlobalConsensusParachainConvertsFor, HashedDescription, + IsConcrete, LocalMint, MatchInClassInstances, NetworkExportTableItem, NoChecking, + ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, + SovereignPaidRemoteExporter, SovereignSignedViaLocation, StartsWith, + StartsWithExplicitGlobalConsensus, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, + WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, + XcmFeeToAccount, }; use xcm_executor::XcmExecutor; @@ -150,10 +150,10 @@ pub type UniquesConvertedConcreteId = assets_common::UniquesConvertedConcreteId; /// Means for transacting unique assets. -pub type UniquesTransactor = RecreateableInstanceAdapter< +pub type UniquesTransactor = UniqueInstancesAdapter< AccountId, LocationToAccountId, - InstancesOfClasses, + MatchInClassInstances, Uniques, >; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index 35784810e35e0..1d339fc22e270 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -45,14 +45,13 @@ use polkadot_runtime_common::xcm_sender::ExponentialPrice; use sp_runtime::traits::{AccountIdConversion, ConvertInto}; use xcm::latest::prelude::*; use xcm_builder::{ - unique_instances::RecreateableInstanceAdapter, AccountId32Aliases, - AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, - AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, - DenyReserveTransferToRelayChain, DenyThenTry, DescribeFamily, DescribePalletTerminal, - EnsureXcmOrigin, FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, - GlobalConsensusParachainConvertsFor, HashedDescription, InstancesOfClasses, IsConcrete, - LocalMint, NetworkExportTableItem, NoChecking, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + unique_instances::UniqueInstancesAdapter, AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, + AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, + AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, DenyThenTry, DescribeFamily, + DescribePalletTerminal, EnsureXcmOrigin, FrameTransactionalProcessor, FungibleAdapter, + FungiblesAdapter, GlobalConsensusParachainConvertsFor, HashedDescription, IsConcrete, + LocalMint, MatchInClassInstances, NetworkExportTableItem, NoChecking, ParentAsSuperuser, + ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, @@ -143,10 +142,10 @@ pub type UniquesConvertedConcreteId = assets_common::UniquesConvertedConcreteId; /// Means for transacting unique assets. -pub type UniquesTransactor = RecreateableInstanceAdapter< +pub type UniquesTransactor = UniqueInstancesAdapter< AccountId, LocationToAccountId, - InstancesOfClasses, + MatchInClassInstances, Uniques, >; diff --git a/polkadot/xcm/xcm-builder/src/asset_conversion.rs b/polkadot/xcm/xcm-builder/src/asset_conversion.rs index 01e565dafa5c8..789615b918f68 100644 --- a/polkadot/xcm/xcm-builder/src/asset_conversion.rs +++ b/polkadot/xcm/xcm-builder/src/asset_conversion.rs @@ -154,20 +154,20 @@ impl< } } -pub struct InstancesOfClasses(PhantomData); +pub struct MatchInClassInstances(PhantomData); impl> - MatchesInstance<(ClassId, InstanceId)> for InstancesOfClasses + MatchesInstance<(ClassId, InstanceId)> for MatchInClassInstances { fn matches_instance(a: &Asset) -> result::Result<(ClassId, InstanceId), MatchError> { Matcher::matches_nonfungibles(a) } } -pub struct ClasslessInstances(PhantomData); +pub struct MatchClasslessInstances(PhantomData); impl> MatchesInstance - for ClasslessInstances + for MatchClasslessInstances { fn matches_instance(a: &Asset) -> result::Result { Matcher::matches_nonfungible(a).ok_or(MatchError::AssetNotHandled) diff --git a/polkadot/xcm/xcm-builder/src/lib.rs b/polkadot/xcm/xcm-builder/src/lib.rs index 786e906759b30..67fd0600ad5b1 100644 --- a/polkadot/xcm/xcm-builder/src/lib.rs +++ b/polkadot/xcm/xcm-builder/src/lib.rs @@ -30,7 +30,7 @@ mod asset_conversion; #[allow(deprecated)] pub use asset_conversion::ConvertedConcreteAssetId; pub use asset_conversion::{ - AsPrefixedGeneralIndex, ClasslessInstances, ConvertedConcreteId, InstancesOfClasses, + AsPrefixedGeneralIndex, ConvertedConcreteId, MatchClasslessInstances, MatchInClassInstances, MatchedConvertedConcreteId, }; diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/adapter.rs b/polkadot/xcm/xcm-builder/src/unique_instances/adapter.rs new file mode 100644 index 0000000000000..052638d49e470 --- /dev/null +++ b/polkadot/xcm/xcm-builder/src/unique_instances/adapter.rs @@ -0,0 +1,155 @@ +use core::marker::PhantomData; +use frame_support::traits::tokens::asset_ops::{ + self, + common_asset_kinds::Instance, + common_strategies::{AssignId, FromTo, IfOwnedBy, Owned}, + AssetDefinition, Create, Destroy, Transfer, +}; +use xcm::latest::prelude::*; +use xcm_executor::traits::{ConvertLocation, Error as MatchError, MatchesInstance, TransactAsset}; + +const LOG_TARGET: &str = "xcm::unique_instances"; + +/// The `UniqueInstancesAdapter` implements the [`TransactAsset`] for unique instances (NFT-like +/// entities), for which the `Matcher` can deduce the instance ID from the XCM [`AssetId`]. +/// +/// The adapter uses the following asset operations: +/// * [`Create`] with the [`Owned`] strategy, which uses the [`AssignId`] approach +/// to assign the instance ID deduced by the `Matcher`. +/// * [`Transfer`] with [`FromTo`] strategy +/// * [`Destroy`] with [`IfOwnedBy`] strategy +/// +/// This adapter assumes that the asset can be safely destroyed +/// without destroying any important data. +/// However, the "destroy" operation can be replaced by another operation. +/// For instance, one can use the [`StashOnDestroy`](super::ops::StashOnDestroy) type to stash the +/// instance instead of destroying it. See other similar types in the [`ops`](super::ops) module. +/// +/// Note on teleports: This adapter doesn't implement teleports since unique instances have +/// associated data that also should be teleported. Currently, neither XCM can transfer such data +/// nor does a standard approach exist in the ecosystem for this use case. +pub struct UniqueInstancesAdapter( + PhantomData<(AccountId, AccountIdConverter, Matcher, InstanceOps)>, +); + +impl TransactAsset + for UniqueInstancesAdapter +where + AccountIdConverter: ConvertLocation, + Matcher: MatchesInstance, + for<'a> InstanceOps: AssetDefinition + + Create>> + + Transfer> + + Destroy>, +{ + fn deposit_asset(what: &Asset, who: &Location, context: Option<&XcmContext>) -> XcmResult { + log::trace!( + target: LOG_TARGET, + "UniqueInstancesAdapter::deposit_asset what: {:?}, who: {:?}, context: {:?}", + what, + who, + context, + ); + + let instance_id = Matcher::matches_instance(what)?; + let who = AccountIdConverter::convert_location(who) + .ok_or(MatchError::AccountIdConversionFailed)?; + + InstanceOps::create(Owned::new(&who, AssignId(&instance_id))) + .map_err(|e| XcmError::FailedToTransactAsset(e.into())) + } + + fn withdraw_asset( + what: &Asset, + who: &Location, + maybe_context: Option<&XcmContext>, + ) -> Result { + log::trace!( + target: LOG_TARGET, + "UniqueInstancesAdapter::withdraw_asset what: {:?}, who: {:?}, context: {:?}", + what, + who, + maybe_context, + ); + let instance_id = Matcher::matches_instance(what)?; + let who = AccountIdConverter::convert_location(who) + .ok_or(MatchError::AccountIdConversionFailed)?; + + InstanceOps::destroy(&instance_id, IfOwnedBy(&who)) + .map_err(|e| XcmError::FailedToTransactAsset(e.into()))?; + + Ok(what.clone().into()) + } + + fn internal_transfer_asset( + what: &Asset, + from: &Location, + to: &Location, + context: &XcmContext, + ) -> Result { + log::trace!( + target: LOG_TARGET, + "UniqueInstancesAdapter::internal_transfer_asset what: {:?}, from: {:?}, to: {:?}, context: {:?}", + what, + from, + to, + context, + ); + + let instance_id = Matcher::matches_instance(what)?; + let from = AccountIdConverter::convert_location(from) + .ok_or(MatchError::AccountIdConversionFailed)?; + let to = AccountIdConverter::convert_location(to) + .ok_or(MatchError::AccountIdConversionFailed)?; + + InstanceOps::transfer(&instance_id, FromTo(&from, &to)) + .map_err(|e| XcmError::FailedToTransactAsset(e.into()))?; + + Ok(what.clone().into()) + } +} + +/// The `UniqueDerivedInstancesAdapter` implements the [`TransactAsset`] to create unique instances +/// (NFT-like entities), for which the `Matcher` can **not** deduce the instance ID from the XCM +/// [`AssetId`]. Instead, this adapter requires the `Matcher` to return +/// the [instance ID assignment approach](asset_ops::IdAssignment) +/// so a new instance can be created using this approach and then deposited to a beneficiary. +pub struct UniqueDerivedInstancesAdapter< + AccountId, + AccountIdConverter, + IdAssignment, + Matcher, + InstanceCreateOp, +>(PhantomData<(AccountId, AccountIdConverter, IdAssignment, Matcher, InstanceCreateOp)>); + +impl TransactAsset + for UniqueDerivedInstancesAdapter< + AccountId, + AccountIdConverter, + IdAssignment, + Matcher, + InstanceCreateOp, + > where + AccountIdConverter: ConvertLocation, + IdAssignment: asset_ops::IdAssignment, + Matcher: MatchesInstance, + for<'a> InstanceCreateOp: Create>, +{ + fn deposit_asset(what: &Asset, who: &Location, context: Option<&XcmContext>) -> XcmResult { + log::trace!( + target: LOG_TARGET, + "UniqueDerivedInstancesAdapter::deposit_asset what: {:?}, who: {:?}, context: {:?}", + what, + who, + context, + ); + + let instance_id_assignment = Matcher::matches_instance(what)?; + let who = AccountIdConverter::convert_location(who) + .ok_or(MatchError::AccountIdConversionFailed)?; + + InstanceCreateOp::create(Owned::new(&who, instance_id_assignment)) + .map(|_reported_id| ()) + .map_err(|e| XcmError::FailedToTransactAsset(e.into())) + } +} diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/backed_derivative.rs b/polkadot/xcm/xcm-builder/src/unique_instances/backed_derivative.rs deleted file mode 100644 index ba53262e16e6d..0000000000000 --- a/polkadot/xcm/xcm-builder/src/unique_instances/backed_derivative.rs +++ /dev/null @@ -1,172 +0,0 @@ -use super::LOG_TARGET; -use core::marker::PhantomData; -use frame_support::traits::{ - tokens::asset_ops::{ - common_asset_kinds::{Class, Instance}, - common_strategies::{DeriveIdFrom, FromTo, Owned}, - AssetDefinition, Create, Transfer, - }, - Get, -}; -use xcm::latest::prelude::*; -use xcm_executor::traits::{ConvertLocation, Error as MatchError, MatchesInstance, TransactAsset}; - -/// The status of a derivative instance. -pub enum DerivativeStatus { - /// The derivative can be deposited (created) in the given class. - DepositableIn(ClassId), - - /// The derivative already exists and it has the given ID. - Exists(InstanceId), -} - -/// The `BackedDerivativeInstanceAdapter` implements the `TransactAsset` for unique instances -/// (NFT-like entities). -/// -/// The adapter uses the following asset operations: -/// * [`Create`] with the [`Owned`] strategy that uses the [`DeriveIdFrom`] id assignment. -/// * The [`DeriveIdFrom`] accepts the value of the `Id` type retrieved from the class [asset -/// definition](AssetDefinition) -/// (`ClassDef` generic parameter, represents a class-like entity such as a collection of NFTs). -/// * [`Transfer`] with [`FromTo`] strategy -/// -/// This adapter assumes that a new asset can be created and an existing asset can be transferred. -/// Also, the adapter assumes that the asset can't be destroyed. -/// So, it transfers the asset to the `StashLocation` on withdrawal. -/// -/// On deposit, the adapter consults the [`DerivativeStatus`] returned from the `Matcher`. -/// If the asset is depositable in a certain class, it will be created within that class. -/// Otherwise, if the asset exists, it will be transferred from the `StashLocation` to the -/// beneficiary. -/// -/// Transfers work as expected, transferring the asset from the `from` location to the beneficiary. -/// -/// This adapter is meant to be used in non-reserve locations where derivatives -/// can't be properly destroyed and then recreated. -/// -/// For instance, an NFT engine on the chain (a pallet or a smart contract) -/// can be incapable of recreating an NFT with the same ID. -/// So, we can only create a derivative with a new ID. -/// In this context, if we burn a derivative on withdrawal: -/// 1. we could exhaust the ID space for derivatives -/// 2. if "burning" means transferring to a special address (like the zero address in EVM), -/// we also waste the active storage space for burned derivatives -/// -/// To avoid that situation, the `StashLocation` is used to hold the withdrawn derivatives. -/// -/// Also, this adapter can be used in the NFT engine that simply doesn't support burning NFTs. -pub struct BackedDerivativeInstanceAdapter< - AccountId, - AccountIdConverter, - Matcher, - ClassDef, - InstanceOps, - StashLocation, ->(PhantomData<(AccountId, AccountIdConverter, Matcher, ClassDef, InstanceOps, StashLocation)>); - -impl TransactAsset - for BackedDerivativeInstanceAdapter< - AccountId, - AccountIdConverter, - Matcher, - ClassDef, - InstanceOps, - StashLocation, - > where - AccountIdConverter: ConvertLocation, - Matcher: MatchesInstance>, - ClassDef: AssetDefinition, - for<'a> InstanceOps: AssetDefinition - + Create, AccountId>> - + Transfer>, - StashLocation: Get, -{ - fn deposit_asset(what: &Asset, who: &Location, context: Option<&XcmContext>) -> XcmResult { - log::trace!( - target: LOG_TARGET, - "BackedDerivativeInstanceAdapter::deposit_asset what: {:?}, who: {:?}, context: {:?}", - what, - who, - context, - ); - - let derivative_status = Matcher::matches_instance(what)?; - let to = AccountIdConverter::convert_location(who) - .ok_or(MatchError::AccountIdConversionFailed)?; - - let result = match derivative_status { - DerivativeStatus::DepositableIn(class_id) => - InstanceOps::create(Owned::new(DeriveIdFrom::parent_id(&class_id), &to)) - .map(|_id| ()), - DerivativeStatus::Exists(instance_id) => { - let from = AccountIdConverter::convert_location(&StashLocation::get()) - .ok_or(MatchError::AccountIdConversionFailed)?; - - InstanceOps::transfer(&instance_id, FromTo(&from, &to)) - }, - }; - - result.map_err(|e| XcmError::FailedToTransactAsset(e.into())) - } - - fn withdraw_asset( - what: &Asset, - who: &Location, - maybe_context: Option<&XcmContext>, - ) -> Result { - log::trace!( - target: LOG_TARGET, - "BackedDerivativeInstanceAdapter::withdraw_asset what: {:?}, who: {:?}, context: {:?}", - what, - who, - maybe_context, - ); - - let derivative_status = Matcher::matches_instance(what)?; - let from = AccountIdConverter::convert_location(who) - .ok_or(MatchError::AccountIdConversionFailed)?; - - if let DerivativeStatus::Exists(instance_id) = derivative_status { - let to = AccountIdConverter::convert_location(&StashLocation::get()) - .ok_or(MatchError::AccountIdConversionFailed)?; - - InstanceOps::transfer(&instance_id, FromTo(&from, &to)) - .map_err(|e| XcmError::FailedToTransactAsset(e.into()))?; - - Ok(what.clone().into()) - } else { - Err(XcmError::NotWithdrawable) - } - } - - fn internal_transfer_asset( - what: &Asset, - from: &Location, - to: &Location, - context: &XcmContext, - ) -> Result { - log::trace!( - target: LOG_TARGET, - "BackedDerivativeInstanceAdapter::internal_transfer_asset what: {:?}, from: {:?}, to: {:?}, context: {:?}", - what, - from, - to, - context, - ); - - let derivative_status = Matcher::matches_instance(what)?; - let from = AccountIdConverter::convert_location(from) - .ok_or(MatchError::AccountIdConversionFailed)?; - let to = AccountIdConverter::convert_location(to) - .ok_or(MatchError::AccountIdConversionFailed)?; - - if let DerivativeStatus::Exists(instance_id) = derivative_status { - InstanceOps::transfer(&instance_id, FromTo(&from, &to)) - .map_err(|e| XcmError::FailedToTransactAsset(e.into()))?; - - Ok(what.clone().into()) - } else { - Err(XcmError::NotWithdrawable) - } - } -} diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs b/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs new file mode 100644 index 0000000000000..2f06c53b08f66 --- /dev/null +++ b/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs @@ -0,0 +1,164 @@ +use core::marker::PhantomData; + +use frame_support::traits::{ + tokens::asset_ops::{ + common_asset_kinds::{Class, Instance}, + common_strategies::{ + AssignId, AutoId, DeriveAndReportId, FromTo, IfOwnedBy, IfRestorable, Owned, + }, + AssetDefinition, Create, CreateStrategy, Destroy, DestroyStrategy, Restore, Stash, + Transfer, TransferStrategy, + }, + TypedGet, +}; +use sp_runtime::{DispatchError, DispatchResult}; +use xcm::latest::prelude::*; + +use super::NonFungibleAsset; + +pub trait TryRegisterDerivative { + fn try_register_derivative( + foreign_asset: &NonFungibleAsset, + instance_id: &InstanceId, + ) -> DispatchResult; + + fn is_derivative_registered(foreign_asset: &NonFungibleAsset) -> bool; +} + +pub trait TryDeregisterDerivative { + fn try_deregister_derivative(instance_id: &InstanceId) -> DispatchResult; + + fn is_derivative(instance_id: &InstanceId) -> bool; +} + +pub struct RegisterDerivativeId { + pub foreign_asset: NonFungibleAsset, + pub instance_id_source: InstanceIdSource, +} + +pub struct RegisterOnCreate(PhantomData<(Registrar, InstanceOps)>); +impl<'a, AccountId, InstanceIdSource, Registrar, InstanceOps> + Create>>> + for RegisterOnCreate +where + Registrar: TryRegisterDerivative, + InstanceOps: AssetDefinition + + for<'b> Create< + Instance, + Owned<'b, AccountId, DeriveAndReportId<'b, InstanceIdSource, InstanceOps::Id>>, + >, +{ + fn create( + strategy: Owned>>, + ) -> DispatchResult { + let Owned { + owner, + id_assignment: AssignId(RegisterDerivativeId { foreign_asset, instance_id_source }), + .. + } = strategy; + + if Registrar::is_derivative_registered(foreign_asset) { + return Err(DispatchError::Other( + "an attempt to register a duplicate of an existing derivative instance", + )); + } + + let instance_id = + InstanceOps::create(Owned::new(owner, DeriveAndReportId::from(instance_id_source)))?; + + Registrar::try_register_derivative(foreign_asset, &instance_id) + } +} + +pub struct DeregisterOnDestroy(PhantomData<(Registrar, InstanceOps)>); +impl AssetDefinition + for DeregisterOnDestroy +where + InstanceOps: AssetDefinition, +{ + type Id = InstanceOps::Id; +} +impl<'a, AccountId, Registrar, InstanceOps> Destroy> + for DeregisterOnDestroy +where + Registrar: TryDeregisterDerivative, + InstanceOps: for<'b> Destroy>, +{ + fn destroy(id: &Self::Id, strategy: IfOwnedBy) -> DispatchResult { + if !Registrar::is_derivative(id) { + return Err(DispatchError::Other( + "an attempt to deregister an instance that isn't a derivative", + )); + } + + InstanceOps::destroy(id, strategy)?; + + Registrar::try_deregister_derivative(id) + } +} + +// pub enum DerivativeInClassStatus { +// Registrable { foreign_asset: NonFungibleAsset, in_class: ClassId }, + +// Restorable(InstanceId), +// } + +// pub enum ClasslessDerivativeStatus { +// Registrable(NonFungibleAsset), + +// Restorable(InstanceId), +// } + +// pub struct RegisterOnCreate(PhantomData<(Registrar, CreateOp, +// RestoreOp)>); impl<'a, AccountId, ClassId, Registrar, CreateOp, RestoreOp> +// Create>>> for RegisterOnCreate +// where +// Registrar: RegisterDerivative, +// CreateOp: AssetDefinition + for<'b> Create>>, RestoreOp: AssetDefinition + for<'c> Restore>, { +// fn create(strategy: Owned>>) -> DispatchResult { let Owned { owner, id_assignment: +// AssignId(status), .. } = strategy; + +// match status { +// DerivativeInClassStatus::Registrable { foreign_asset, in_class } => { +// let instance_id = +// CreateOp::create(Owned::new(owner, DeriveAndReportId::from(in_class)))?; + +// Registrar::register_derivative(foreign_asset, &instance_id)?; +// }, +// DerivativeInClassStatus::Restorable(instance_id) => +// RestoreOp::restore(instance_id, IfRestorable(owner))?, +// } + +// Ok(()) +// } +// } +// impl<'a, AccountId, Registrar, CreateOp, RestoreOp> +// Create>>> for RegisterOnCreate where +// Registrar: RegisterDerivative, +// CreateOp: AssetDefinition + for<'b> Create>>, RestoreOp: AssetDefinition + for<'c> +// Restore>, { +// fn create(strategy: Owned>>) +// -> DispatchResult { let Owned { owner, id_assignment: AssignId(status), .. } = +// strategy; + +// match status { +// ClasslessDerivativeStatus::Registrable(foreign_asset) => { +// let instance_id = +// CreateOp::create(Owned::new(owner, AutoId::new()))?; + +// Registrar::register_derivative(foreign_asset, &instance_id)?; +// }, +// ClasslessDerivativeStatus::Restorable(instance_id) => +// RestoreOp::restore(instance_id, IfRestorable(owner))?, +// } + +// Ok(()) +// } +// } diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/mod.rs b/polkadot/xcm/xcm-builder/src/unique_instances/mod.rs index 1134a04208add..ccee5b33f76c4 100644 --- a/polkadot/xcm/xcm-builder/src/unique_instances/mod.rs +++ b/polkadot/xcm/xcm-builder/src/unique_instances/mod.rs @@ -1,35 +1,14 @@ -use frame_support::traits::tokens::asset_ops::{ - common_asset_kinds::Instance, common_strategies::FromTo, Transfer, -}; use xcm::latest::prelude::*; -use xcm_executor::traits::{ConvertLocation, Error as MatchError, MatchesInstance}; -const LOG_TARGET: &str = "xcm::unique_instances"; +pub mod adapter; +pub mod derivatives; +pub mod ops; -pub mod backed_derivative; -pub mod recreateable; -pub mod transferable; +pub use adapter::*; +pub use derivatives::*; +pub use ops::*; -pub use backed_derivative::*; -pub use recreateable::*; -pub use transferable::*; - -fn transfer_instance< - AccountId, - AccountIdConverter: ConvertLocation, - Matcher: MatchesInstance, - InstanceTransfer: for<'a> Transfer>, ->( - what: &Asset, - from: &Location, - to: &Location, -) -> XcmResult { - let instance_id = Matcher::matches_instance(what)?; - let from = - AccountIdConverter::convert_location(from).ok_or(MatchError::AccountIdConversionFailed)?; - let to = - AccountIdConverter::convert_location(to).ok_or(MatchError::AccountIdConversionFailed)?; - - InstanceTransfer::transfer(&instance_id, FromTo(&from, &to)) - .map_err(|e| XcmError::FailedToTransactAsset(e.into())) +pub struct NonFungibleAsset { + pub id: AssetId, + pub instance: AssetInstance, } diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/ops.rs b/polkadot/xcm/xcm-builder/src/unique_instances/ops.rs new file mode 100644 index 0000000000000..1c3e2aa79dd9e --- /dev/null +++ b/polkadot/xcm/xcm-builder/src/unique_instances/ops.rs @@ -0,0 +1,120 @@ +use core::marker::PhantomData; +use frame_support::traits::{ + tokens::asset_ops::{ + common_asset_kinds::Instance, + common_strategies::{AssignId, DeriveAndReportId, FromTo, IfOwnedBy, IfRestorable, Owned}, + AssetDefinition, Create, CreateStrategy, Destroy, DestroyStrategy, Restore, Stash, + Transfer, TransferStrategy, + }, + TypedGet, +}; +use sp_runtime::{DispatchError, DispatchResult}; + +pub struct UniqueInstancesOps( + PhantomData<(CreateOp, TransferOp, DestroyOp)>, +); +impl AssetDefinition + for UniqueInstancesOps +where + TransferOp: AssetDefinition, + DestroyOp: AssetDefinition, +{ + type Id = TransferOp::Id; +} +impl Create + for UniqueInstancesOps +where + Strategy: CreateStrategy, + CreateOp: Create, +{ + fn create(strategy: Strategy) -> Result { + CreateOp::create(strategy) + } +} +impl Transfer + for UniqueInstancesOps +where + Strategy: TransferStrategy, + TransferOp: Transfer, + DestroyOp: AssetDefinition, +{ + fn transfer(id: &Self::Id, strategy: Strategy) -> DispatchResult { + TransferOp::transfer(id, strategy) + } +} +impl Destroy + for UniqueInstancesOps +where + Strategy: DestroyStrategy, + TransferOp: AssetDefinition, + DestroyOp: AssetDefinition + Destroy, +{ + fn destroy(id: &Self::Id, strategy: Strategy) -> Result { + DestroyOp::destroy(id, strategy) + } +} + +pub struct SimpleStash(PhantomData<(StashAccount, InstanceOps)>); +impl AssetDefinition for SimpleStash +where + InstanceOps: AssetDefinition, +{ + type Id = InstanceOps::Id; +} +impl<'a, StashAccount, InstanceOps> Stash> + for SimpleStash +where + StashAccount: TypedGet, + InstanceOps: for<'b> Transfer>, +{ + fn stash( + id: &Self::Id, + IfOwnedBy(possible_owner): IfOwnedBy, + ) -> DispatchResult { + InstanceOps::transfer(id, FromTo(possible_owner, &StashAccount::get())) + } +} +impl<'a, StashAccount, InstanceOps> Restore> + for SimpleStash +where + StashAccount: TypedGet, + InstanceOps: for<'b> Transfer>, +{ + fn restore( + id: &Self::Id, + IfRestorable(owner): IfRestorable, + ) -> DispatchResult { + InstanceOps::transfer(id, FromTo(&StashAccount::get(), owner)) + } +} + +pub struct RestoreOnCreate(PhantomData); +impl<'a, AccountId, InstanceOps> + Create>> + for RestoreOnCreate +where + InstanceOps: for<'b> Restore>, +{ + fn create(strategy: Owned>) -> DispatchResult { + let Owned { owner, id_assignment: AssignId(instance_id), .. } = strategy; + + InstanceOps::restore(instance_id, IfRestorable(owner)) + } +} + +pub struct StashOnDestroy(PhantomData); +impl AssetDefinition for StashOnDestroy +where + InstanceOps: AssetDefinition, +{ + type Id = InstanceOps::Id; +} +impl<'a, AccountId, InstanceOps> Destroy> + for StashOnDestroy +where + InstanceOps: for<'b> Stash>, +{ + fn destroy(id: &Self::Id, strategy: IfOwnedBy<'a, AccountId>) -> DispatchResult { + InstanceOps::stash(id, strategy) + } +} diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/recreateable.rs b/polkadot/xcm/xcm-builder/src/unique_instances/recreateable.rs deleted file mode 100644 index 8426f7711eab8..0000000000000 --- a/polkadot/xcm/xcm-builder/src/unique_instances/recreateable.rs +++ /dev/null @@ -1,109 +0,0 @@ -use super::{transfer_instance, LOG_TARGET}; -use core::marker::PhantomData; -use frame_support::traits::tokens::asset_ops::{ - common_asset_kinds::Instance, - common_strategies::{FromTo, IfOwnedBy, Owned, PredefinedId}, - AssetDefinition, Create, Destroy, Transfer, -}; -use xcm::latest::prelude::*; -use xcm_executor::traits::{ConvertLocation, Error as MatchError, MatchesInstance, TransactAsset}; - -/// The `RecreateableInstanceAdapter` implements the `TransactAsset` for unique instances (NFT-like -/// entities). -/// The adapter uses the following asset operations: -/// * [`Create`] with the [`Owned`] strategy that uses the [`PredefinedId`]. -/// The `Id` used is the one from the [asset's definition](AssetDefinition). -/// * [`Transfer`] with [`FromTo`] strategy -/// * [`Destroy`] with [`IfOwnedBy`] strategy -/// -/// This adapter assumes that the asset can be safely destroyed -/// without the loss of any important data. It will destroy the asset on withdrawal. -/// Similarly, it assumes that the asset can be recreated with the same ID on deposit. -/// -/// Transfers work without additional assumptions. -/// -/// If the above assumptions are true, -/// this adapter can be used both to work with the original instances in a reserve location -/// and to work with derivatives in other locations. -/// -/// Only the assets matched by `Matcher` are affected. -/// If the `Matcher` recognizes the instance, it should return its `Id`. -/// -/// Note on teleports: -/// This adapter doesn't implement teleports at the moment since unique instances have associated -/// data that should be teleported along. -/// Currently, neither XCM has the ability to transfer such data -/// nor a standard approach exists in the ecosystem for this use case. -pub struct RecreateableInstanceAdapter( - PhantomData<(AccountId, AccountIdConverter, Matcher, InstanceOps)>, -); - -impl TransactAsset - for RecreateableInstanceAdapter -where - AccountIdConverter: ConvertLocation, - Matcher: MatchesInstance, - for<'a> InstanceOps: AssetDefinition - + Create, AccountId>> - + Transfer> - + Destroy>, -{ - fn deposit_asset(what: &Asset, who: &Location, context: Option<&XcmContext>) -> XcmResult { - log::trace!( - target: LOG_TARGET, - "RecreateableInstanceAdapter::deposit_asset what: {:?}, who: {:?}, context: {:?}", - what, - who, - context, - ); - - let instance_id = Matcher::matches_instance(what)?; - let who = AccountIdConverter::convert_location(who) - .ok_or(MatchError::AccountIdConversionFailed)?; - - InstanceOps::create(Owned::new(PredefinedId(&instance_id), &who)) - .map_err(|e| XcmError::FailedToTransactAsset(e.into())) - } - - fn withdraw_asset( - what: &Asset, - who: &Location, - maybe_context: Option<&XcmContext>, - ) -> Result { - log::trace!( - target: LOG_TARGET, - "RecreateableInstanceAdapter::withdraw_asset what: {:?}, who: {:?}, context: {:?}", - what, - who, - maybe_context, - ); - let instance_id = Matcher::matches_instance(what)?; - let who = AccountIdConverter::convert_location(who) - .ok_or(MatchError::AccountIdConversionFailed)?; - - InstanceOps::destroy(&instance_id, IfOwnedBy(&who)) - .map_err(|e| XcmError::FailedToTransactAsset(e.into()))?; - - Ok(what.clone().into()) - } - - fn internal_transfer_asset( - what: &Asset, - from: &Location, - to: &Location, - context: &XcmContext, - ) -> Result { - log::trace!( - target: LOG_TARGET, - "RecreateableInstanceAdapter::internal_transfer_asset what: {:?}, from: {:?}, to: {:?}, context: {:?}", - what, - from, - to, - context, - ); - - transfer_instance::(what, from, to)?; - - Ok(what.clone().into()) - } -} diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/transferable.rs b/polkadot/xcm/xcm-builder/src/unique_instances/transferable.rs deleted file mode 100644 index 2d2b0ced780e1..0000000000000 --- a/polkadot/xcm/xcm-builder/src/unique_instances/transferable.rs +++ /dev/null @@ -1,108 +0,0 @@ -use super::{transfer_instance, LOG_TARGET}; -use core::marker::PhantomData; -use frame_support::traits::{ - tokens::asset_ops::{common_asset_kinds::Instance, common_strategies::FromTo, Transfer}, - Get, -}; -use xcm::latest::prelude::*; -use xcm_executor::traits::{ConvertLocation, MatchesInstance, TransactAsset}; - -/// The `TransferableInstanceAdapter` implements the `TransactAsset` for unique instances (NFT-like -/// entities). -/// -/// The adapter uses only the [`Transfer`] asset operation with the [`FromTo`] strategy. -/// -/// It is meant to be used when the asset can't be safely destroyed on withdrawal -/// (i.e., the absence of the loss of important data can't be guaranteed when the asset is -/// destroyed). Equivalently, this adapter may be used when the asset can't be recreated on deposit. -/// -/// The adapter uses the `StashLocation` as the beneficiary to transfer the asset on withdrawal. -/// On deposit, the asset will be transferred from the `StashLocation` to the beneficiary. -/// -/// Transfers work as expected, transferring the asset from the `from` location to the beneficiary. -/// -/// This adapter can be used only in a reserve location. -/// It can't create new instances, hence it can't create derivatives. -pub struct TransferableInstanceAdapter< - AccountId, - AccountIdConverter, - Matcher, - InstanceTransfer, - StashLocation, ->(PhantomData<(AccountId, AccountIdConverter, Matcher, InstanceTransfer, StashLocation)>); - -impl< - AccountId, - AccountIdConverter: ConvertLocation, - Matcher: MatchesInstance, - InstanceTransfer: for<'a> Transfer>, - StashLocation: Get, - > TransactAsset - for TransferableInstanceAdapter< - AccountId, - AccountIdConverter, - Matcher, - InstanceTransfer, - StashLocation, - > -{ - fn deposit_asset(what: &Asset, who: &Location, context: Option<&XcmContext>) -> XcmResult { - log::trace!( - target: LOG_TARGET, - "TransferableInstanceAdapter::deposit_asset what: {:?}, who: {:?}, context: {:?}", - what, - who, - context, - ); - - transfer_instance::( - what, - &StashLocation::get(), - who, - ) - } - - fn withdraw_asset( - what: &Asset, - who: &Location, - maybe_context: Option<&XcmContext>, - ) -> Result { - log::trace!( - target: LOG_TARGET, - "TransferableInstanceAdapter::withdraw_asset what: {:?}, who: {:?}, context: {:?}", - what, - who, - maybe_context, - ); - - transfer_instance::( - what, - who, - &StashLocation::get(), - )?; - - Ok(what.clone().into()) - } - - fn internal_transfer_asset( - what: &Asset, - from: &Location, - to: &Location, - context: &XcmContext, - ) -> Result { - log::trace!( - target: LOG_TARGET, - "TransferableInstanceAdapter::internal_transfer_asset what: {:?}, from: {:?}, to: {:?}, context: {:?}", - what, - from, - to, - context, - ); - - transfer_instance::( - what, from, to, - )?; - - Ok(what.clone().into()) - } -} From a725bbd71e12c4a5ea7d376eee133a21ff419f2c Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Tue, 11 Jun 2024 23:43:36 +0200 Subject: [PATCH 07/41] fix: add derives to NonFungibleAsset --- polkadot/xcm/xcm-builder/src/unique_instances/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/mod.rs b/polkadot/xcm/xcm-builder/src/unique_instances/mod.rs index ccee5b33f76c4..c5ef0d9c0dbaa 100644 --- a/polkadot/xcm/xcm-builder/src/unique_instances/mod.rs +++ b/polkadot/xcm/xcm-builder/src/unique_instances/mod.rs @@ -1,3 +1,5 @@ +use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; use xcm::latest::prelude::*; pub mod adapter; @@ -8,6 +10,7 @@ pub use adapter::*; pub use derivatives::*; pub use ops::*; +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)] pub struct NonFungibleAsset { pub id: AssetId, pub instance: AssetInstance, From b4893dfa917b42260bb62b4b2ab457e203e48dcf Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Tue, 11 Jun 2024 23:44:08 +0200 Subject: [PATCH 08/41] chore: remove unneeded --- .../src/unique_instances/derivatives.rs | 66 ------------------- 1 file changed, 66 deletions(-) diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs b/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs index 2f06c53b08f66..c911e77958dce 100644 --- a/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs +++ b/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs @@ -96,69 +96,3 @@ where Registrar::try_deregister_derivative(id) } } - -// pub enum DerivativeInClassStatus { -// Registrable { foreign_asset: NonFungibleAsset, in_class: ClassId }, - -// Restorable(InstanceId), -// } - -// pub enum ClasslessDerivativeStatus { -// Registrable(NonFungibleAsset), - -// Restorable(InstanceId), -// } - -// pub struct RegisterOnCreate(PhantomData<(Registrar, CreateOp, -// RestoreOp)>); impl<'a, AccountId, ClassId, Registrar, CreateOp, RestoreOp> -// Create>>> for RegisterOnCreate -// where -// Registrar: RegisterDerivative, -// CreateOp: AssetDefinition + for<'b> Create>>, RestoreOp: AssetDefinition + for<'c> Restore>, { -// fn create(strategy: Owned>>) -> DispatchResult { let Owned { owner, id_assignment: -// AssignId(status), .. } = strategy; - -// match status { -// DerivativeInClassStatus::Registrable { foreign_asset, in_class } => { -// let instance_id = -// CreateOp::create(Owned::new(owner, DeriveAndReportId::from(in_class)))?; - -// Registrar::register_derivative(foreign_asset, &instance_id)?; -// }, -// DerivativeInClassStatus::Restorable(instance_id) => -// RestoreOp::restore(instance_id, IfRestorable(owner))?, -// } - -// Ok(()) -// } -// } -// impl<'a, AccountId, Registrar, CreateOp, RestoreOp> -// Create>>> for RegisterOnCreate where -// Registrar: RegisterDerivative, -// CreateOp: AssetDefinition + for<'b> Create>>, RestoreOp: AssetDefinition + for<'c> -// Restore>, { -// fn create(strategy: Owned>>) -// -> DispatchResult { let Owned { owner, id_assignment: AssignId(status), .. } = -// strategy; - -// match status { -// ClasslessDerivativeStatus::Registrable(foreign_asset) => { -// let instance_id = -// CreateOp::create(Owned::new(owner, AutoId::new()))?; - -// Registrar::register_derivative(foreign_asset, &instance_id)?; -// }, -// ClasslessDerivativeStatus::Restorable(instance_id) => -// RestoreOp::restore(instance_id, IfRestorable(owner))?, -// } - -// Ok(()) -// } -// } From 105ebfaef9f7554b497eea787db52fc62192275e Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Wed, 12 Jun 2024 20:53:44 +0200 Subject: [PATCH 09/41] feat: add derives to id assignments --- substrate/frame/support/src/traits/tokens/asset_ops.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/substrate/frame/support/src/traits/tokens/asset_ops.rs b/substrate/frame/support/src/traits/tokens/asset_ops.rs index 819e7933352d5..f196a81502eeb 100644 --- a/substrate/frame/support/src/traits/tokens/asset_ops.rs +++ b/substrate/frame/support/src/traits/tokens/asset_ops.rs @@ -250,6 +250,9 @@ pub mod common_asset_kinds { /// This modules contains the common asset ops strategies. pub mod common_strategies { use super::*; + use codec::{Decode, Encode, MaxEncodedLen}; + use scale_info::TypeInfo; + use sp_runtime::RuntimeDebug; /// The `WithOrigin` is a strategy that accepts a runtime origin and the `Inner` strategy. /// @@ -439,6 +442,7 @@ pub mod common_strategies { /// /// It accepts the `Id` type of the asset. /// The "create" strategy should report the value of type `Id` upon successful asset creation. + #[derive(RuntimeDebug, PartialEq, Eq, Clone, Encode, Decode, MaxEncodedLen, TypeInfo)] pub struct AutoId(PhantomData); impl AutoId { pub fn new() -> Self { @@ -454,6 +458,7 @@ pub mod common_strategies { /// /// It accepts `Params` to assign an ID to the newly created asset. /// This ID assignment approach doesn't report the ID upon the asset's creation. + #[derive(RuntimeDebug, PartialEq, Eq, Clone, Encode, Decode, MaxEncodedLen, TypeInfo)] pub struct AssignId<'a, Params>(pub &'a Params); impl<'a, Params> IdAssignment for AssignId<'a, Params> { type ReportedId = (); @@ -469,6 +474,7 @@ pub mod common_strategies { /// /// An example of ID derivation is the creation of an NFT inside a collection using the /// collection ID as `Params`. The `Id` in this case is the full ID of the NFT. + #[derive(RuntimeDebug, PartialEq, Eq, Clone, Encode, Decode, MaxEncodedLen, TypeInfo)] pub struct DeriveAndReportId<'a, Params, Id>(pub &'a Params, pub PhantomData); impl<'a, Params, Id> DeriveAndReportId<'a, Params, Id> { pub fn from(params: &'a Params) -> Self { From 379d6cd863ef775a35cc14de6bfcb6de2315be1f Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Thu, 13 Jun 2024 20:35:37 +0200 Subject: [PATCH 10/41] refactor(asset-ops): lifetimes in common strategies --- .../src/unique_instances/adapter.rs | 18 +-- .../src/unique_instances/derivatives.rs | 17 ++- .../xcm-builder/src/unique_instances/ops.rs | 26 ++--- .../support/src/traits/tokens/asset_ops.rs | 110 +++++++++--------- .../frame/uniques/src/impl_asset_ops/class.rs | 57 ++++----- .../uniques/src/impl_asset_ops/instance.rs | 44 ++++--- 6 files changed, 124 insertions(+), 148 deletions(-) diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/adapter.rs b/polkadot/xcm/xcm-builder/src/unique_instances/adapter.rs index 052638d49e470..ed2a329490cff 100644 --- a/polkadot/xcm/xcm-builder/src/unique_instances/adapter.rs +++ b/polkadot/xcm/xcm-builder/src/unique_instances/adapter.rs @@ -37,10 +37,10 @@ impl TransactAsset where AccountIdConverter: ConvertLocation, Matcher: MatchesInstance, - for<'a> InstanceOps: AssetDefinition - + Create>> - + Transfer> - + Destroy>, + InstanceOps: AssetDefinition + + Create>> + + Transfer> + + Destroy>, { fn deposit_asset(what: &Asset, who: &Location, context: Option<&XcmContext>) -> XcmResult { log::trace!( @@ -55,7 +55,7 @@ where let who = AccountIdConverter::convert_location(who) .ok_or(MatchError::AccountIdConversionFailed)?; - InstanceOps::create(Owned::new(&who, AssignId(&instance_id))) + InstanceOps::create(Owned::new(who, AssignId(instance_id))) .map_err(|e| XcmError::FailedToTransactAsset(e.into())) } @@ -75,7 +75,7 @@ where let who = AccountIdConverter::convert_location(who) .ok_or(MatchError::AccountIdConversionFailed)?; - InstanceOps::destroy(&instance_id, IfOwnedBy(&who)) + InstanceOps::destroy(&instance_id, IfOwnedBy(who)) .map_err(|e| XcmError::FailedToTransactAsset(e.into()))?; Ok(what.clone().into()) @@ -102,7 +102,7 @@ where let to = AccountIdConverter::convert_location(to) .ok_or(MatchError::AccountIdConversionFailed)?; - InstanceOps::transfer(&instance_id, FromTo(&from, &to)) + InstanceOps::transfer(&instance_id, FromTo(from, to)) .map_err(|e| XcmError::FailedToTransactAsset(e.into()))?; Ok(what.clone().into()) @@ -133,7 +133,7 @@ impl Tra AccountIdConverter: ConvertLocation, IdAssignment: asset_ops::IdAssignment, Matcher: MatchesInstance, - for<'a> InstanceCreateOp: Create>, + InstanceCreateOp: Create>, { fn deposit_asset(what: &Asset, who: &Location, context: Option<&XcmContext>) -> XcmResult { log::trace!( @@ -148,7 +148,7 @@ impl Tra let who = AccountIdConverter::convert_location(who) .ok_or(MatchError::AccountIdConversionFailed)?; - InstanceCreateOp::create(Owned::new(&who, instance_id_assignment)) + InstanceCreateOp::create(Owned::new(who, instance_id_assignment)) .map(|_reported_id| ()) .map_err(|e| XcmError::FailedToTransactAsset(e.into())) } diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs b/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs index c911e77958dce..15d106f7aac64 100644 --- a/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs +++ b/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs @@ -37,16 +37,13 @@ pub struct RegisterDerivativeId { } pub struct RegisterOnCreate(PhantomData<(Registrar, InstanceOps)>); -impl<'a, AccountId, InstanceIdSource, Registrar, InstanceOps> - Create>>> +impl + Create>>> for RegisterOnCreate where Registrar: TryRegisterDerivative, InstanceOps: AssetDefinition - + for<'b> Create< - Instance, - Owned<'b, AccountId, DeriveAndReportId<'b, InstanceIdSource, InstanceOps::Id>>, - >, + + Create>>, { fn create( strategy: Owned>>, @@ -57,7 +54,7 @@ where .. } = strategy; - if Registrar::is_derivative_registered(foreign_asset) { + if Registrar::is_derivative_registered(&foreign_asset) { return Err(DispatchError::Other( "an attempt to register a duplicate of an existing derivative instance", )); @@ -66,7 +63,7 @@ where let instance_id = InstanceOps::create(Owned::new(owner, DeriveAndReportId::from(instance_id_source)))?; - Registrar::try_register_derivative(foreign_asset, &instance_id) + Registrar::try_register_derivative(&foreign_asset, &instance_id) } } @@ -78,11 +75,11 @@ where { type Id = InstanceOps::Id; } -impl<'a, AccountId, Registrar, InstanceOps> Destroy> +impl Destroy> for DeregisterOnDestroy where Registrar: TryDeregisterDerivative, - InstanceOps: for<'b> Destroy>, + InstanceOps: Destroy>, { fn destroy(id: &Self::Id, strategy: IfOwnedBy) -> DispatchResult { if !Registrar::is_derivative(id) { diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/ops.rs b/polkadot/xcm/xcm-builder/src/unique_instances/ops.rs index 1c3e2aa79dd9e..ac949529619fe 100644 --- a/polkadot/xcm/xcm-builder/src/unique_instances/ops.rs +++ b/polkadot/xcm/xcm-builder/src/unique_instances/ops.rs @@ -61,44 +61,43 @@ where { type Id = InstanceOps::Id; } -impl<'a, StashAccount, InstanceOps> Stash> +impl Stash> for SimpleStash where StashAccount: TypedGet, - InstanceOps: for<'b> Transfer>, + InstanceOps: Transfer>, { fn stash( id: &Self::Id, IfOwnedBy(possible_owner): IfOwnedBy, ) -> DispatchResult { - InstanceOps::transfer(id, FromTo(possible_owner, &StashAccount::get())) + InstanceOps::transfer(id, FromTo(possible_owner, StashAccount::get())) } } -impl<'a, StashAccount, InstanceOps> Restore> +impl Restore> for SimpleStash where StashAccount: TypedGet, - InstanceOps: for<'b> Transfer>, + InstanceOps: Transfer>, { fn restore( id: &Self::Id, IfRestorable(owner): IfRestorable, ) -> DispatchResult { - InstanceOps::transfer(id, FromTo(&StashAccount::get(), owner)) + InstanceOps::transfer(id, FromTo(StashAccount::get(), owner)) } } pub struct RestoreOnCreate(PhantomData); -impl<'a, AccountId, InstanceOps> - Create>> +impl Create>> for RestoreOnCreate where - InstanceOps: for<'b> Restore>, + InstanceOps: Restore>, { fn create(strategy: Owned>) -> DispatchResult { let Owned { owner, id_assignment: AssignId(instance_id), .. } = strategy; - InstanceOps::restore(instance_id, IfRestorable(owner)) + InstanceOps::restore(&instance_id, IfRestorable(owner)) } } @@ -109,12 +108,11 @@ where { type Id = InstanceOps::Id; } -impl<'a, AccountId, InstanceOps> Destroy> - for StashOnDestroy +impl Destroy> for StashOnDestroy where - InstanceOps: for<'b> Stash>, + InstanceOps: Stash>, { - fn destroy(id: &Self::Id, strategy: IfOwnedBy<'a, AccountId>) -> DispatchResult { + fn destroy(id: &Self::Id, strategy: IfOwnedBy) -> DispatchResult { InstanceOps::stash(id, strategy) } } diff --git a/substrate/frame/support/src/traits/tokens/asset_ops.rs b/substrate/frame/support/src/traits/tokens/asset_ops.rs index f196a81502eeb..f25d1e8231c8d 100644 --- a/substrate/frame/support/src/traits/tokens/asset_ops.rs +++ b/substrate/frame/support/src/traits/tokens/asset_ops.rs @@ -291,18 +291,18 @@ pub mod common_strategies { /// It accepts whatever parameters are set in its generic argument. /// For instance, for an unchecked transfer, /// this strategy may take a reference to a beneficiary account. - pub struct JustDo<'a, Params = ()>(pub &'a Params); - impl<'a> Default for JustDo<'a, ()> { + pub struct JustDo(pub Params); + impl Default for JustDo<()> { fn default() -> Self { - Self(&()) + Self(()) } } - impl<'a, Owner> TransferStrategy for JustDo<'a, Owner> {} - impl<'a> DestroyStrategy for JustDo<'a> { + impl TransferStrategy for JustDo {} + impl DestroyStrategy for JustDo { type Success = (); } - impl<'a> StashStrategy for JustDo<'a> {} - impl<'a, Owner> RestoreStrategy for JustDo<'a, Owner> {} + impl StashStrategy for JustDo {} + impl RestoreStrategy for JustDo {} /// The `Bytes` strategy represents raw metadata bytes. /// It is both an [inspect](MetadataInspectStrategy) and [update](MetadataUpdateStrategy) @@ -459,8 +459,8 @@ pub mod common_strategies { /// It accepts `Params` to assign an ID to the newly created asset. /// This ID assignment approach doesn't report the ID upon the asset's creation. #[derive(RuntimeDebug, PartialEq, Eq, Clone, Encode, Decode, MaxEncodedLen, TypeInfo)] - pub struct AssignId<'a, Params>(pub &'a Params); - impl<'a, Params> IdAssignment for AssignId<'a, Params> { + pub struct AssignId(pub Params); + impl IdAssignment for AssignId { type ReportedId = (); } @@ -475,13 +475,13 @@ pub mod common_strategies { /// An example of ID derivation is the creation of an NFT inside a collection using the /// collection ID as `Params`. The `Id` in this case is the full ID of the NFT. #[derive(RuntimeDebug, PartialEq, Eq, Clone, Encode, Decode, MaxEncodedLen, TypeInfo)] - pub struct DeriveAndReportId<'a, Params, Id>(pub &'a Params, pub PhantomData); - impl<'a, Params, Id> DeriveAndReportId<'a, Params, Id> { - pub fn from(params: &'a Params) -> Self { + pub struct DeriveAndReportId(pub Params, pub PhantomData); + impl DeriveAndReportId { + pub fn from(params: Params) -> Self { Self(params, PhantomData) } } - impl<'a, ParentId, Id> IdAssignment for DeriveAndReportId<'a, ParentId, Id> { + impl IdAssignment for DeriveAndReportId { type ReportedId = Id; } @@ -495,28 +495,24 @@ pub mod common_strategies { /// /// The [`Success`](CreateStrategy::Success) will contain /// the [reported ID](IdAssignment::ReportedId) of the ID assignment approach. - pub struct Owned<'a, Owner, Assignment: IdAssignment, Config = (), Witness = ()> { - pub owner: &'a Owner, + pub struct Owned { + pub owner: Owner, pub id_assignment: Assignment, - pub config: &'a Config, - pub witness: &'a Witness, + pub config: Config, + pub witness: Witness, } - impl<'a, Owner, Assignment: IdAssignment> Owned<'a, Owner, Assignment, (), ()> { - pub fn new(owner: &'a Owner, id_assignment: Assignment) -> Self { - Self { id_assignment, owner, config: &(), witness: &() } + impl Owned { + pub fn new(owner: Owner, id_assignment: Assignment) -> Self { + Self { id_assignment, owner, config: (), witness: () } } } - impl<'a, Owner, Assignment: IdAssignment, Config> Owned<'a, Owner, Assignment, Config, ()> { - pub fn new_configured( - owner: &'a Owner, - id_assignment: Assignment, - config: &'a Config, - ) -> Self { - Self { id_assignment, owner, config, witness: &() } + impl Owned { + pub fn new_configured(owner: Owner, id_assignment: Assignment, config: Config) -> Self { + Self { id_assignment, owner, config, witness: () } } } - impl<'a, Owner, Assignment: IdAssignment, Config, Witness> CreateStrategy - for Owned<'a, Owner, Assignment, Config, Witness> + impl CreateStrategy + for Owned { type Success = Assignment::ReportedId; } @@ -532,30 +528,30 @@ pub mod common_strategies { /// /// The [`Success`](CreateStrategy::Success) will contain /// the [reported ID](IdAssignment::ReportedId) of the ID assignment approach. - pub struct Adminable<'a, Account, Assignment: IdAssignment, Config = (), Witness = ()> { - pub owner: &'a Account, - pub admin: &'a Account, + pub struct Adminable { + pub owner: Account, + pub admin: Account, pub id_assignment: Assignment, - pub config: &'a Config, - pub witness: &'a Witness, + pub config: Config, + pub witness: Witness, } - impl<'a, Account, Assignment: IdAssignment> Adminable<'a, Account, Assignment, (), ()> { - pub fn new(id_assignment: Assignment, owner: &'a Account, admin: &'a Account) -> Self { - Self { id_assignment, owner, admin, config: &(), witness: &() } + impl Adminable { + pub fn new(id_assignment: Assignment, owner: Account, admin: Account) -> Self { + Self { id_assignment, owner, admin, config: (), witness: () } } } - impl<'a, Account, Assignment: IdAssignment, Config> Adminable<'a, Account, Assignment, Config, ()> { + impl Adminable { pub fn new_configured( - owner: &'a Account, - admin: &'a Account, + owner: Account, + admin: Account, id_assignment: Assignment, - config: &'a Config, + config: Config, ) -> Self { - Self { id_assignment, owner, admin, config, witness: &() } + Self { id_assignment, owner, admin, config, witness: () } } } - impl<'a, Account, Assignment: IdAssignment, Config, Witness> CreateStrategy - for Adminable<'a, Account, Assignment, Config, Witness> + impl CreateStrategy + for Adminable { type Success = Assignment::ReportedId; } @@ -563,19 +559,19 @@ pub mod common_strategies { /// The `FromTo` is a [`transfer strategy`](TransferStrategy). /// /// It accepts two parameters: `from` and `to` whom the asset should be transferred. - pub struct FromTo<'a, Owner>(pub &'a Owner, pub &'a Owner); - impl<'a, Owner> TransferStrategy for FromTo<'a, Owner> {} + pub struct FromTo(pub Owner, pub Owner); + impl TransferStrategy for FromTo {} /// The `IfOwnedBy` is both a [`destroy strategy`](DestroyStrategy) /// and a [`stash strategy`](StashStrategy). /// /// It accepts a possible owner of the asset. /// If the provided entity owns the asset, the corresponding operation will be performed. - pub struct IfOwnedBy<'a, Owner>(pub &'a Owner); - impl<'a, Owner> DestroyStrategy for IfOwnedBy<'a, Owner> { + pub struct IfOwnedBy(pub Owner); + impl DestroyStrategy for IfOwnedBy { type Success = (); } - impl<'a, Owner> StashStrategy for IfOwnedBy<'a, Owner> {} + impl StashStrategy for IfOwnedBy {} /// The `IfRestorable` is a [`restore strategy`](RestoreStrategy). /// @@ -583,26 +579,26 @@ pub mod common_strategies { /// For instance, if an asset is restorable, /// this strategy may reference a beneficiary account, /// which should own the asset upon restoration. - pub struct IfRestorable<'a, Params>(pub &'a Params); - impl<'a, Params> RestoreStrategy for IfRestorable<'a, Params> {} + pub struct IfRestorable(pub Params); + impl RestoreStrategy for IfRestorable {} /// The `WithWitness` is a [`destroy strategy`](DestroyStrategy). /// /// It accepts a `Witness` to destroy an asset. /// It will also return a `Witness` value upon destruction. - pub struct WithWitness<'a, Witness>(pub &'a Witness); - impl<'a, Witness> DestroyStrategy for WithWitness<'a, Witness> { + pub struct WithWitness(pub Witness); + impl DestroyStrategy for WithWitness { type Success = Witness; } /// The `IfOwnedByWithWitness` is a [`destroy strategy`](DestroyStrategy). /// /// It is a combination of the [`IfOwnedBy`] and the [`WithWitness`] strategies. - pub struct IfOwnedByWithWitness<'a, Owner, Witness> { - pub owner: &'a Owner, - pub witness: &'a Witness, + pub struct IfOwnedByWithWitness { + pub owner: Owner, + pub witness: Witness, } - impl<'a, Owner, Witness> DestroyStrategy for IfOwnedByWithWitness<'a, Owner, Witness> { + impl DestroyStrategy for IfOwnedByWithWitness { type Success = Witness; } } diff --git a/substrate/frame/uniques/src/impl_asset_ops/class.rs b/substrate/frame/uniques/src/impl_asset_ops/class.rs index f3efbbd79f97a..fba000848c07c 100644 --- a/substrate/frame/uniques/src/impl_asset_ops/class.rs +++ b/substrate/frame/uniques/src/impl_asset_ops/class.rs @@ -56,8 +56,8 @@ impl<'a, T: Config, I: 'static> InspectMetadata>> } } -impl<'a, T: Config, I: 'static> - Create>> for Pallet +impl, I: 'static> Create>> + for Pallet { fn create(strategy: Adminable>) -> DispatchResult { let Adminable { owner, admin, id_assignment: AssignId(collection), .. } = strategy; @@ -68,32 +68,25 @@ impl<'a, T: Config, I: 'static> admin.clone(), T::CollectionDeposit::get(), false, - Event::Created { - collection: collection.clone(), - creator: owner.clone(), - owner: admin.clone(), - }, + Event::Created { collection, creator: owner, owner: admin }, ) } } -impl<'a, T: Config, I: 'static> - Create< - Class, - WithOrigin>>, - > for Pallet +impl, I: 'static> + Create>>> + for Pallet { fn create( strategy: WithOrigin>>, ) -> DispatchResult { - let WithOrigin( - origin, - creation @ Adminable { owner, id_assignment: AssignId(collection), .. }, - ) = strategy; + let WithOrigin(origin, creation) = strategy; + + let Adminable { owner, id_assignment: AssignId(collection), .. } = &creation; let maybe_check_signer = T::ForceOrigin::try_origin(origin).map(|_| None).or_else(|origin| { - T::CreateOrigin::ensure_origin(origin, &collection) + T::CreateOrigin::ensure_origin(origin, collection) .map(Some) .map_err(DispatchError::from) })?; @@ -106,25 +99,23 @@ impl<'a, T: Config, I: 'static> } } -impl<'a, T: Config, I: 'static> Destroy> - for Pallet -{ +impl, I: 'static> Destroy> for Pallet { fn destroy( collection: &Self::Id, strategy: WithWitness, ) -> Result { let WithWitness(witness) = strategy; - Self::do_destroy_collection(collection.clone(), *witness, None) + Self::do_destroy_collection(collection.clone(), witness, None) } } -impl<'a, T: Config, I: 'static> - Destroy>> for Pallet +impl, I: 'static> + Destroy>> for Pallet { fn destroy( collection: &Self::Id, - strategy: WithOrigin>, + strategy: WithOrigin>, ) -> Result { let WithOrigin(origin, destroy) = strategy; @@ -134,8 +125,8 @@ impl<'a, T: Config, I: 'static> } } -impl<'a, T: Config, I: 'static> - Destroy> for Pallet +impl, I: 'static> Destroy> + for Pallet { fn destroy( collection: &Self::Id, @@ -143,15 +134,13 @@ impl<'a, T: Config, I: 'static> ) -> Result { let IfOwnedByWithWitness { owner, witness } = strategy; - Self::do_destroy_collection(collection.clone(), *witness, Some(owner.clone())) + Self::do_destroy_collection(collection.clone(), witness, Some(owner)) } } -impl<'a, T: Config, I: 'static> - Destroy< - Class, - WithOrigin>, - > for Pallet +impl, I: 'static> + Destroy>> + for Pallet { fn destroy( collection: &Self::Id, @@ -164,9 +153,9 @@ impl<'a, T: Config, I: 'static> }; if let Some(signer) = maybe_check_owner { - ensure!(signer == *owner, Error::::NoPermission); + ensure!(signer == owner, Error::::NoPermission); } - Self::do_destroy_collection(collection.clone(), *witness, Some(owner.clone())) + Self::do_destroy_collection(collection.clone(), witness, Some(owner)) } } diff --git a/substrate/frame/uniques/src/impl_asset_ops/instance.rs b/substrate/frame/uniques/src/impl_asset_ops/instance.rs index ac4cb2443df9d..9a41f9685eced 100644 --- a/substrate/frame/uniques/src/impl_asset_ops/instance.rs +++ b/substrate/frame/uniques/src/impl_asset_ops/instance.rs @@ -69,26 +69,22 @@ impl, I: 'static> InspectMetadata for Pallet } } -impl<'a, T: Config, I: 'static> - Create>> - for Pallet +impl, I: 'static> + Create>> for Pallet { fn create( strategy: Owned>, ) -> DispatchResult { let Owned { owner, id_assignment: AssignId((collection, item)), .. } = strategy; - Self::do_mint(collection.clone(), *item, owner.clone(), |_| Ok(())) + Self::do_mint(collection, item, owner, |_| Ok(())) } } -impl<'a, T: Config, I: 'static> +impl, I: 'static> Create< Instance, - WithOrigin< - T::RuntimeOrigin, - Owned<'a, T::AccountId, AssignId<'a, (T::CollectionId, T::ItemId)>>, - >, + WithOrigin>>, > for Pallet { fn create( @@ -102,23 +98,23 @@ impl<'a, T: Config, I: 'static> let signer = ensure_signed(origin)?; - Self::do_mint(collection.clone(), *item, owner.clone(), |collection_details| { + Self::do_mint(collection, item, owner, |collection_details| { ensure!(collection_details.issuer == signer, Error::::NoPermission); Ok(()) }) } } -impl<'a, T: Config, I: 'static> Transfer> for Pallet { +impl, I: 'static> Transfer> for Pallet { fn transfer((collection, item): &Self::Id, strategy: JustDo) -> DispatchResult { let JustDo(dest) = strategy; - Self::do_transfer(collection.clone(), *item, dest.clone(), |_, _| Ok(())) + Self::do_transfer(collection.clone(), *item, dest, |_, _| Ok(())) } } -impl<'a, T: Config, I: 'static> - Transfer>> for Pallet +impl, I: 'static> + Transfer>> for Pallet { fn transfer( (collection, item): &Self::Id, @@ -138,24 +134,24 @@ impl<'a, T: Config, I: 'static> } } -impl<'a, T: Config, I: 'static> Transfer> for Pallet { +impl, I: 'static> Transfer> for Pallet { fn transfer((collection, item): &Self::Id, strategy: FromTo) -> DispatchResult { let FromTo(from, to) = strategy; Self::do_transfer(collection.clone(), *item, to.clone(), |_, details| { - ensure!(details.owner == *from, Error::::WrongOwner); + ensure!(details.owner == from, Error::::WrongOwner); Ok(()) }) } } -impl, I: 'static> Destroy> for Pallet { +impl, I: 'static> Destroy for Pallet { fn destroy((collection, item): &Self::Id, _strategy: JustDo) -> DispatchResult { Self::do_burn(collection.clone(), *item, |_, _| Ok(())) } } -impl<'a, T: Config, I: 'static> Destroy>> +impl, I: 'static> Destroy> for Pallet { fn destroy( @@ -166,23 +162,23 @@ impl<'a, T: Config, I: 'static> Destroy::get(collection, item).ok_or(Error::::UnknownCollection)?; - >::destroy(id, WithOrigin(origin, IfOwnedBy(&details.owner))) + >::destroy(id, WithOrigin(origin, IfOwnedBy(details.owner))) } } -impl<'a, T: Config, I: 'static> Destroy> for Pallet { +impl, I: 'static> Destroy> for Pallet { fn destroy((collection, item): &Self::Id, strategy: IfOwnedBy) -> DispatchResult { let IfOwnedBy(who) = strategy; Self::do_burn(collection.clone(), *item, |_, d| { - ensure!(d.owner == *who, Error::::NoPermission); + ensure!(d.owner == who, Error::::NoPermission); Ok(()) }) } } -impl<'a, T: Config, I: 'static> - Destroy>> for Pallet +impl, I: 'static> + Destroy>> for Pallet { fn destroy( (collection, item): &Self::Id, @@ -195,7 +191,7 @@ impl<'a, T: Config, I: 'static> Self::do_burn(collection.clone(), *item, |collection_details, details| { let is_permitted = collection_details.admin == signer || details.owner == signer; ensure!(is_permitted, Error::::NoPermission); - ensure!(*who == details.owner, Error::::WrongOwner); + ensure!(who == details.owner, Error::::WrongOwner); Ok(()) }) } From d28ad756d040f4d0ba6d47ef7f033e7a5feaa34a Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Sat, 15 Jun 2024 02:23:41 +0200 Subject: [PATCH 11/41] refactor: xcm unique-instances derivatives traits --- .../src/unique_instances/derivatives.rs | 120 +++++++++++++----- .../xcm-builder/src/unique_instances/mod.rs | 6 +- 2 files changed, 87 insertions(+), 39 deletions(-) diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs b/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs index 15d106f7aac64..14d3627522c06 100644 --- a/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs +++ b/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs @@ -1,47 +1,46 @@ use core::marker::PhantomData; -use frame_support::traits::{ - tokens::asset_ops::{ - common_asset_kinds::{Class, Instance}, - common_strategies::{ - AssignId, AutoId, DeriveAndReportId, FromTo, IfOwnedBy, IfRestorable, Owned, +use frame_support::{ + ensure, + traits::{ + tokens::asset_ops::{ + common_asset_kinds::{Class, Instance}, + common_strategies::{ + AssignId, AutoId, DeriveAndReportId, FromTo, IfOwnedBy, IfRestorable, Owned, + }, + AssetDefinition, Create, CreateStrategy, Destroy, DestroyStrategy, Restore, Stash, + Transfer, TransferStrategy, }, - AssetDefinition, Create, CreateStrategy, Destroy, DestroyStrategy, Restore, Stash, - Transfer, TransferStrategy, + TypedGet, }, - TypedGet, }; use sp_runtime::{DispatchError, DispatchResult}; use xcm::latest::prelude::*; +use xcm_executor::traits::{Error, MatchesInstance}; use super::NonFungibleAsset; -pub trait TryRegisterDerivative { - fn try_register_derivative( - foreign_asset: &NonFungibleAsset, - instance_id: &InstanceId, - ) -> DispatchResult; +pub trait DerivativesRegistry { + fn try_register_derivative(original: &Original, derivative: &Derivative) -> DispatchResult; - fn is_derivative_registered(foreign_asset: &NonFungibleAsset) -> bool; -} + fn try_deregister_derivative(derivative: &Derivative) -> DispatchResult; -pub trait TryDeregisterDerivative { - fn try_deregister_derivative(instance_id: &InstanceId) -> DispatchResult; + fn get_derivative(original: &Original) -> Option; - fn is_derivative(instance_id: &InstanceId) -> bool; + fn get_original(derivative: &Derivative) -> Option; } pub struct RegisterDerivativeId { - pub foreign_asset: NonFungibleAsset, + pub foreign_nonfungible: NonFungibleAsset, pub instance_id_source: InstanceIdSource, } -pub struct RegisterOnCreate(PhantomData<(Registrar, InstanceOps)>); -impl +pub struct RegisterOnCreate(PhantomData<(Registry, InstanceOps)>); +impl Create>>> - for RegisterOnCreate + for RegisterOnCreate where - Registrar: TryRegisterDerivative, + Registry: DerivativesRegistry, InstanceOps: AssetDefinition + Create>>, { @@ -50,11 +49,12 @@ where ) -> DispatchResult { let Owned { owner, - id_assignment: AssignId(RegisterDerivativeId { foreign_asset, instance_id_source }), + id_assignment: + AssignId(RegisterDerivativeId { foreign_nonfungible, instance_id_source }), .. } = strategy; - if Registrar::is_derivative_registered(&foreign_asset) { + if Registry::get_derivative(&foreign_nonfungible).is_some() { return Err(DispatchError::Other( "an attempt to register a duplicate of an existing derivative instance", )); @@ -63,26 +63,25 @@ where let instance_id = InstanceOps::create(Owned::new(owner, DeriveAndReportId::from(instance_id_source)))?; - Registrar::try_register_derivative(&foreign_asset, &instance_id) + Registry::try_register_derivative(&foreign_nonfungible, &instance_id) } } -pub struct DeregisterOnDestroy(PhantomData<(Registrar, InstanceOps)>); -impl AssetDefinition - for DeregisterOnDestroy +pub struct DeregisterOnDestroy(PhantomData<(Registry, InstanceOps)>); +impl AssetDefinition for DeregisterOnDestroy where InstanceOps: AssetDefinition, { type Id = InstanceOps::Id; } -impl Destroy> - for DeregisterOnDestroy +impl Destroy> + for DeregisterOnDestroy where - Registrar: TryDeregisterDerivative, + Registry: DerivativesRegistry, InstanceOps: Destroy>, { fn destroy(id: &Self::Id, strategy: IfOwnedBy) -> DispatchResult { - if !Registrar::is_derivative(id) { + if Registry::get_original(id).is_none() { return Err(DispatchError::Other( "an attempt to deregister an instance that isn't a derivative", )); @@ -90,6 +89,59 @@ where InstanceOps::destroy(id, strategy)?; - Registrar::try_deregister_derivative(id) + Registry::try_deregister_derivative(id) + } +} + +pub struct MatchDerivativeIdSources(PhantomData); +impl, DerivativeIdSource> + MatchesInstance>> + for MatchDerivativeIdSources +{ + fn matches_instance( + asset: &Asset, + ) -> Result>, Error> { + match asset.fun { + Fungibility::NonFungible(asset_instance) => { + let instance_id_source = + Registry::get_derivative(&asset.id).ok_or(Error::AssetNotHandled)?; + + Ok(AssignId(RegisterDerivativeId { + foreign_nonfungible: (asset.id.clone(), asset_instance), + instance_id_source, + })) + }, + Fungibility::Fungible(_) => Err(Error::AssetNotHandled), + } + } +} + +pub struct MatchDerivativeInstances(PhantomData); +impl, DerivativeId> + MatchesInstance for MatchDerivativeInstances +{ + fn matches_instance(asset: &Asset) -> Result { + match asset.fun { + Fungibility::NonFungible(asset_instance) => + Registry::get_derivative(&(asset.id.clone(), asset_instance)) + .ok_or(Error::AssetNotHandled), + Fungibility::Fungible(_) => Err(Error::AssetNotHandled), + } + } +} + +pub struct EnsureNotDerivativeInstance(PhantomData<(Registry, Matcher)>); +impl< + Registry: DerivativesRegistry, + Matcher: MatchesInstance, + DerivativeId, + > MatchesInstance for EnsureNotDerivativeInstance +{ + fn matches_instance(asset: &Asset) -> Result { + let instance_id = Matcher::matches_instance(asset)?; + + ensure!(Registry::get_original(&instance_id).is_none(), Error::AssetNotHandled,); + + Ok(instance_id) } } diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/mod.rs b/polkadot/xcm/xcm-builder/src/unique_instances/mod.rs index c5ef0d9c0dbaa..6f24cd5581d11 100644 --- a/polkadot/xcm/xcm-builder/src/unique_instances/mod.rs +++ b/polkadot/xcm/xcm-builder/src/unique_instances/mod.rs @@ -10,8 +10,4 @@ pub use adapter::*; pub use derivatives::*; pub use ops::*; -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)] -pub struct NonFungibleAsset { - pub id: AssetId, - pub instance: AssetInstance, -} +pub type NonFungibleAsset = (AssetId, AssetInstance); From c45c7ca9b0acd12920ad97f1bcc1382c626532b3 Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Mon, 24 Jun 2024 19:27:07 +0200 Subject: [PATCH 12/41] refactor: id assignment --- .../src/unique_instances/adapter.rs | 42 +++--- .../src/unique_instances/derivatives.rs | 129 ++++++++++++++---- .../xcm-builder/src/unique_instances/ops.rs | 48 ++++++- .../support/src/traits/tokens/asset_ops.rs | 63 +++++---- .../frame/uniques/src/impl_asset_ops/class.rs | 34 +++-- .../uniques/src/impl_asset_ops/instance.rs | 35 +++-- 6 files changed, 243 insertions(+), 108 deletions(-) diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/adapter.rs b/polkadot/xcm/xcm-builder/src/unique_instances/adapter.rs index ed2a329490cff..077466357a024 100644 --- a/polkadot/xcm/xcm-builder/src/unique_instances/adapter.rs +++ b/polkadot/xcm/xcm-builder/src/unique_instances/adapter.rs @@ -2,7 +2,7 @@ use core::marker::PhantomData; use frame_support::traits::tokens::asset_ops::{ self, common_asset_kinds::Instance, - common_strategies::{AssignId, FromTo, IfOwnedBy, Owned}, + common_strategies::{DeriveAndReportId, FromTo, IfOwnedBy, Owned, PredefinedId}, AssetDefinition, Create, Destroy, Transfer, }; use xcm::latest::prelude::*; @@ -14,7 +14,7 @@ const LOG_TARGET: &str = "xcm::unique_instances"; /// entities), for which the `Matcher` can deduce the instance ID from the XCM [`AssetId`]. /// /// The adapter uses the following asset operations: -/// * [`Create`] with the [`Owned`] strategy, which uses the [`AssignId`] approach +/// * [`Create`] with the [`Owned`] strategy, which uses the [`PredefinedId`] approach /// to assign the instance ID deduced by the `Matcher`. /// * [`Transfer`] with [`FromTo`] strategy /// * [`Destroy`] with [`IfOwnedBy`] strategy @@ -38,7 +38,7 @@ where AccountIdConverter: ConvertLocation, Matcher: MatchesInstance, InstanceOps: AssetDefinition - + Create>> + + Create>> + Transfer> + Destroy>, { @@ -55,7 +55,8 @@ where let who = AccountIdConverter::convert_location(who) .ok_or(MatchError::AccountIdConversionFailed)?; - InstanceOps::create(Owned::new(who, AssignId(instance_id))) + InstanceOps::create(Owned::new(who, PredefinedId::from(instance_id))) + .map(|_reported_id| ()) .map_err(|e| XcmError::FailedToTransactAsset(e.into())) } @@ -109,46 +110,49 @@ where } } -/// The `UniqueDerivedInstancesAdapter` implements the [`TransactAsset`] to create unique instances +/// The `UniqueInstancesDepositAdapter` implements the [`TransactAsset`] to create unique instances /// (NFT-like entities), for which the `Matcher` can **not** deduce the instance ID from the XCM /// [`AssetId`]. Instead, this adapter requires the `Matcher` to return -/// the [instance ID assignment approach](asset_ops::IdAssignment) -/// so a new instance can be created using this approach and then deposited to a beneficiary. -pub struct UniqueDerivedInstancesAdapter< +/// the derive ID parameters (the `DeriveIdParams`) for the [`DeriveAndReportId`] ID assignment +/// approach. +/// +/// The new instance will be created using the `InstanceCreateOp` and then deposited to a +/// beneficiary. +pub struct UniqueInstancesDepositAdapter< AccountId, AccountIdConverter, - IdAssignment, + DeriveIdParams, Matcher, InstanceCreateOp, ->(PhantomData<(AccountId, AccountIdConverter, IdAssignment, Matcher, InstanceCreateOp)>); +>(PhantomData<(AccountId, AccountIdConverter, DeriveIdParams, Matcher, InstanceCreateOp)>); -impl TransactAsset - for UniqueDerivedInstancesAdapter< +impl TransactAsset + for UniqueInstancesDepositAdapter< AccountId, AccountIdConverter, - IdAssignment, + DeriveIdParams, Matcher, InstanceCreateOp, > where AccountIdConverter: ConvertLocation, - IdAssignment: asset_ops::IdAssignment, - Matcher: MatchesInstance, - InstanceCreateOp: Create>, + Matcher: MatchesInstance, + InstanceCreateOp: AssetDefinition + + Create>>, { fn deposit_asset(what: &Asset, who: &Location, context: Option<&XcmContext>) -> XcmResult { log::trace!( target: LOG_TARGET, - "UniqueDerivedInstancesAdapter::deposit_asset what: {:?}, who: {:?}, context: {:?}", + "UniqueInstancesDepositAdapter::deposit_asset what: {:?}, who: {:?}, context: {:?}", what, who, context, ); - let instance_id_assignment = Matcher::matches_instance(what)?; + let derive_id_params = Matcher::matches_instance(what)?; let who = AccountIdConverter::convert_location(who) .ok_or(MatchError::AccountIdConversionFailed)?; - InstanceCreateOp::create(Owned::new(who, instance_id_assignment)) + InstanceCreateOp::create(Owned::new(who, DeriveAndReportId::from(derive_id_params))) .map(|_reported_id| ()) .map_err(|e| XcmError::FailedToTransactAsset(e.into())) } diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs b/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs index 14d3627522c06..cc93183cfc747 100644 --- a/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs +++ b/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs @@ -1,12 +1,13 @@ -use core::marker::PhantomData; +//! Utilities for working with unique instances derivatives. +use core::marker::PhantomData; use frame_support::{ ensure, traits::{ tokens::asset_ops::{ common_asset_kinds::{Class, Instance}, common_strategies::{ - AssignId, AutoId, DeriveAndReportId, FromTo, IfOwnedBy, IfRestorable, Owned, + AutoId, DeriveAndReportId, FromTo, IfOwnedBy, IfRestorable, Owned, }, AssetDefinition, Create, CreateStrategy, Destroy, DestroyStrategy, Restore, Stash, Transfer, TransferStrategy, @@ -20,6 +21,12 @@ use xcm_executor::traits::{Error, MatchesInstance}; use super::NonFungibleAsset; +/// A registry abstracts the mapping between an `Original` entity and a `Derivative` entity. +/// +/// The primary use cases of the registry are: +/// * a map between a [`NonFungibleAsset`] and a derivative instance ID +/// * a map between an [`AssetId`] and a derive ID parameters for the [`DeriveAndReportId`] +/// to create a new derivative instance pub trait DerivativesRegistry { fn try_register_derivative(original: &Original, derivative: &Derivative) -> DispatchResult; @@ -30,29 +37,59 @@ pub trait DerivativesRegistry { fn get_original(derivative: &Derivative) -> Option; } -pub struct RegisterDerivativeId { +/// Parameters for registering a new derivative instance. +pub struct DerivativeRegisterParams { + /// The XCM identified of the original unique instance. pub foreign_nonfungible: NonFungibleAsset, - pub instance_id_source: InstanceIdSource, + + /// The derive ID parameters for the [`DeriveAndReportId`] + /// to create a new derivative instance. + pub derivative_id_params: DerivativeIdParams, } +/// The `RegisterOnCreate` is a utility for creating a new instance +/// and immediately binding it to the original instance using the [`DerivativesRegistry`]. +/// +/// It implements the [`Create`] operation using the [`Owned`] strategy +/// and the [`DeriveAndReportId`] ID assignment, accepting the [`DerivativeRegisterParams`] as the +/// parameters. +/// +/// The `RegisterOnCreate` will create a new derivative instance using the `InstanceOps` +/// and then bind it to the original instance via the `Registry`'s +/// [`try_register_derivative`](DerivativesRegistry::try_register_derivative). +/// +/// The `InstanceOps` must be capable of creating a new instance by deriving the ID +/// based on the [`derivative_id_params`](DerivativeRegisterParams::derivative_id_params). pub struct RegisterOnCreate(PhantomData<(Registry, InstanceOps)>); -impl - Create>>> - for RegisterOnCreate +impl AssetDefinition for RegisterOnCreate +where + InstanceOps: AssetDefinition, +{ + type Id = InstanceOps::Id; +} +impl + Create< + Instance, + Owned< + AccountId, + DeriveAndReportId, InstanceOps::Id>, + >, + > for RegisterOnCreate where Registry: DerivativesRegistry, InstanceOps: AssetDefinition - + Create>>, + + Create>>, { fn create( - strategy: Owned>>, - ) -> DispatchResult { - let Owned { - owner, - id_assignment: - AssignId(RegisterDerivativeId { foreign_nonfungible, instance_id_source }), - .. - } = strategy; + strategy: Owned< + AccountId, + DeriveAndReportId, InstanceOps::Id>, + >, + ) -> Result { + let Owned { owner, id_assignment, .. } = strategy; + + let DerivativeRegisterParams { foreign_nonfungible, derivative_id_params } = + id_assignment.params; if Registry::get_derivative(&foreign_nonfungible).is_some() { return Err(DispatchError::Other( @@ -61,12 +98,22 @@ where } let instance_id = - InstanceOps::create(Owned::new(owner, DeriveAndReportId::from(instance_id_source)))?; + InstanceOps::create(Owned::new(owner, DeriveAndReportId::from(derivative_id_params)))?; - Registry::try_register_derivative(&foreign_nonfungible, &instance_id) + Registry::try_register_derivative(&foreign_nonfungible, &instance_id)?; + + Ok(instance_id) } } +/// The `DeregisterOnDestroy` is a utility for destroying a derivative instance +/// and immediately removing its binding to the original instance via the [`DerivativesRegistry`]. +/// +/// It implements the [`Destroy`] operation using the [`IfOwnedBy`] strategy. +/// +/// The `DeregisterOnDestroy` will destroy a derivative instance using the `InstanceOps` +/// and then unbind it from the original instance via the `Registry`'s +/// [`try_deregister_derivative`](DerivativesRegistry::try_deregister_derivative). pub struct DeregisterOnDestroy(PhantomData<(Registry, InstanceOps)>); impl AssetDefinition for DeregisterOnDestroy where @@ -93,29 +140,38 @@ where } } -pub struct MatchDerivativeIdSources(PhantomData); -impl, DerivativeIdSource> - MatchesInstance>> - for MatchDerivativeIdSources +/// The `MatchDerivativeRegisterParams` is an XCM Matcher +/// that returns [the parameters](DerivativeRegisterParams) for registering a new derivative +/// instance. +/// +/// This Matcher can be used in the +/// [`UniqueInstancesDepositAdapter`](super::UniqueInstancesDepositAdapter). +pub struct MatchDerivativeRegisterParams(PhantomData); +impl, DerivativeIdParams> + MatchesInstance> + for MatchDerivativeRegisterParams { fn matches_instance( asset: &Asset, - ) -> Result>, Error> { + ) -> Result, Error> { match asset.fun { Fungibility::NonFungible(asset_instance) => { - let instance_id_source = + let derivative_id_params = Registry::get_derivative(&asset.id).ok_or(Error::AssetNotHandled)?; - Ok(AssignId(RegisterDerivativeId { + Ok(DerivativeRegisterParams { foreign_nonfungible: (asset.id.clone(), asset_instance), - instance_id_source, - })) + derivative_id_params, + }) }, Fungibility::Fungible(_) => Err(Error::AssetNotHandled), } } } +/// The `MatchDerivativeInstances` is an XCM Matcher +/// that uses a [`DerivativesRegistry`] to match the XCM identification of the original instance +/// to a derivative instance. pub struct MatchDerivativeInstances(PhantomData); impl, DerivativeId> MatchesInstance for MatchDerivativeInstances @@ -130,6 +186,25 @@ impl, DerivativeId } } +/// The `EnsureNotDerivativeInstance` is an XCM Matcher that +/// ensures that the instance returned by the inner `Matcher` isn't a derivative. +/// +/// The check is performed using the [`DerivativesRegistry`]. +/// +/// This Matcher is needed if derivative instances are created within the same NFT engine +/// as this chain's original instances, +/// i.e. if addressing a derivative instance using the local XCM identification is possible. +/// +/// For example, suppose this chain's original instances (for which this chain is the reserve +/// location) can be addressed like this `id: PalletInstance(111)/GeneralIndex(), fun: +/// NonFungible(Index())`. So, this chain is the reserve location for all +/// instances matching the above identification. +/// +/// However, if some of the instances within Pallet #111 could be derivatives as well, +/// we need to ensure that this chain won't act as the reserve location for these instances. +/// If we allow this, this chain could send a derivative as if it were the original NFT on this +/// chain. The other chain can't know that this instance isn't the original. +/// We must prevent that so this chain will act as an honest reserve location. pub struct EnsureNotDerivativeInstance(PhantomData<(Registry, Matcher)>); impl< Registry: DerivativesRegistry, diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/ops.rs b/polkadot/xcm/xcm-builder/src/unique_instances/ops.rs index ac949529619fe..6eb9443b5ca5f 100644 --- a/polkadot/xcm/xcm-builder/src/unique_instances/ops.rs +++ b/polkadot/xcm/xcm-builder/src/unique_instances/ops.rs @@ -1,8 +1,12 @@ +//! Utilities for redefining and auto-implementing the unique instances operations. + use core::marker::PhantomData; use frame_support::traits::{ tokens::asset_ops::{ common_asset_kinds::Instance, - common_strategies::{AssignId, DeriveAndReportId, FromTo, IfOwnedBy, IfRestorable, Owned}, + common_strategies::{ + DeriveAndReportId, FromTo, IfOwnedBy, IfRestorable, Owned, PredefinedId, + }, AssetDefinition, Create, CreateStrategy, Destroy, DestroyStrategy, Restore, Stash, Transfer, TransferStrategy, }, @@ -10,6 +14,11 @@ use frame_support::traits::{ }; use sp_runtime::{DispatchError, DispatchResult}; +/// The `UniqueInstancesOps` allows the creation of a new "NFT engine" capable of creating, +/// transferring, and destroying the unique instances by merging three distinct implementations. +/// +/// The resulting "NFT engine" can be used in the +/// [`UniqueInstancesAdapter`](super::UniqueInstancesAdapter). pub struct UniqueInstancesOps( PhantomData<(CreateOp, TransferOp, DestroyOp)>, ); @@ -54,6 +63,11 @@ where } } +/// The `SimpleStash` implements both the [`Stash`] and [`Restore`] operations +/// by utilizing the [`Transfer`] operation. +/// Stashing with the [`IfOwnedBy`] strategy is implemented as the transfer to the stash account +/// using the [`FromTo`] strategy. Restoring with the [`IfRestorable`] is implemented symmetrically +/// as the transfer from the stash account using the [`FromTo`] strategy. pub struct SimpleStash(PhantomData<(StashAccount, InstanceOps)>); impl AssetDefinition for SimpleStash where @@ -88,19 +102,43 @@ where } } +/// The `RestoreOnCreate` implements the [`Create`] operation by utilizing the [`Restore`] +/// operation. The creation is implemented using the [`Owned`] strategy with the [`PredefinedId`] ID +/// assignment. Such creation is modeled by the [`Restore`] operation using the [`IfRestorable`] +/// strategy. +/// +/// The implemented [`Create`] operation can be used in the +/// [`UniqueInstancesAdapter`](super::UniqueInstancesAdapter) via the [`UniqueInstancesOps`]. pub struct RestoreOnCreate(PhantomData); -impl Create>> +impl AssetDefinition for RestoreOnCreate +where + InstanceOps: AssetDefinition, +{ + type Id = InstanceOps::Id; +} +impl Create>> for RestoreOnCreate where InstanceOps: Restore>, { - fn create(strategy: Owned>) -> DispatchResult { - let Owned { owner, id_assignment: AssignId(instance_id), .. } = strategy; + fn create( + strategy: Owned>, + ) -> Result { + let Owned { owner, id_assignment, .. } = strategy; + let instance_id = id_assignment.params; + + InstanceOps::restore(&instance_id, IfRestorable(owner))?; - InstanceOps::restore(&instance_id, IfRestorable(owner)) + Ok(instance_id) } } +/// The `StashOnDestroy` implements the [`Destroy`] operation by utilizing the [`Stash`] operation. +/// The destroy operation is implemented using the [`IfOwnedBy`] strategy +/// and modeled by the [`Stash`] operation using the same strategy. +/// +/// The implemented [`Destroy`] operation can be used in the +/// [`UniqueInstancesAdapter`](super::UniqueInstancesAdapter) via the [`UniqueInstancesOps`]. pub struct StashOnDestroy(PhantomData); impl AssetDefinition for StashOnDestroy where diff --git a/substrate/frame/support/src/traits/tokens/asset_ops.rs b/substrate/frame/support/src/traits/tokens/asset_ops.rs index f25d1e8231c8d..c4d8863bfcae1 100644 --- a/substrate/frame/support/src/traits/tokens/asset_ops.rs +++ b/substrate/frame/support/src/traits/tokens/asset_ops.rs @@ -125,15 +125,17 @@ pub trait CreateStrategy { /// /// The common ID assignments are: /// * [`AutoId`](common_strategies::AutoId) -/// * [`AssignId`](common_strategies::AssignId) +/// * [`PredefinedId`](common_strategies::PredefinedId) /// * [`DeriveAndReportId`](common_strategies::DeriveAndReportId) pub trait IdAssignment { /// The reported ID type. /// /// Examples: - /// * [`AutoId`](common_strategies::AutoId) returns ID of the newly created asset - /// * [`AssignId`](common_strategies::AssignId) doesn't report an ID, i.e., returns `()` - /// * [`DeriveAndReportId`](common_strategies::DeriveAndReportId) returns the derived ID + /// * [`AutoId`](common_strategies::AutoId) returns the ID of the newly created asset + /// * [`PredefinedId`](common_strategies::PredefinedId) accepts the ID to be assigned to the + /// newly created asset + /// * [`DeriveAndReportId`](common_strategies::DeriveAndReportId) returns the ID derived from + /// the input parameters type ReportedId; } @@ -215,7 +217,7 @@ pub trait Stash: AssetDefinition } /// A strategy for use in the [`Restore`] implementations. -/// The common stash strategies are: +/// The common restore strategies are: /// * [`JustDo`](common_strategies::JustDo) /// * [`IfRestorable`](common_strategies::IfRestorable) pub trait RestoreStrategy {} @@ -441,48 +443,45 @@ pub mod common_strategies { /// [`"create" strategies`](CreateStrategy). /// /// It accepts the `Id` type of the asset. - /// The "create" strategy should report the value of type `Id` upon successful asset creation. - #[derive(RuntimeDebug, PartialEq, Eq, Clone, Encode, Decode, MaxEncodedLen, TypeInfo)] - pub struct AutoId(PhantomData); - impl AutoId { - pub fn new() -> Self { - Self(PhantomData) - } - } - impl IdAssignment for AutoId { - type ReportedId = Id; - } + /// The "create" strategy should report the value of type `ReportedId` upon successful asset + /// creation. + pub type AutoId = DeriveAndReportId<(), ReportedId>; - /// The `AssignId` is an ID assignment approach intended to be used in + /// The `PredefinedId` is an ID assignment approach intended to be used in /// [`"create" strategies`](CreateStrategy). /// - /// It accepts `Params` to assign an ID to the newly created asset. - /// This ID assignment approach doesn't report the ID upon the asset's creation. - #[derive(RuntimeDebug, PartialEq, Eq, Clone, Encode, Decode, MaxEncodedLen, TypeInfo)] - pub struct AssignId(pub Params); - impl IdAssignment for AssignId { - type ReportedId = (); - } + /// It accepts the `Id` that should be assigned to the newly created asset. + /// + /// The "create" strategy should report the `Id` value upon successful asset creation. + pub type PredefinedId = DeriveAndReportId; /// The `DeriveAndReportId` is an ID assignment approach intended to be used in /// [`"create" strategies`](CreateStrategy). /// /// It accepts the `Params` and the `Id`. - /// The `Id` value should be computed by the "create" strategy using the `Params` value. + /// The `ReportedId` value should be computed by the "create" strategy using the `Params` value. /// - /// The "create" strategy should report the `Id` value upon successful asset creation. + /// The "create" strategy should report the `ReportedId` value upon successful asset creation. /// /// An example of ID derivation is the creation of an NFT inside a collection using the - /// collection ID as `Params`. The `Id` in this case is the full ID of the NFT. + /// collection ID as `Params`. The `ReportedId` in this case is the full ID of the NFT. #[derive(RuntimeDebug, PartialEq, Eq, Clone, Encode, Decode, MaxEncodedLen, TypeInfo)] - pub struct DeriveAndReportId(pub Params, pub PhantomData); - impl DeriveAndReportId { + pub struct DeriveAndReportId { + pub params: Params, + _phantom: PhantomData, + } + impl DeriveAndReportId<(), ReportedId> { + pub fn auto() -> AutoId { + Self { params: (), _phantom: PhantomData } + } + } + impl DeriveAndReportId { pub fn from(params: Params) -> Self { - Self(params, PhantomData) + Self { params, _phantom: PhantomData } } } - impl IdAssignment for DeriveAndReportId { - type ReportedId = Id; + impl IdAssignment for DeriveAndReportId { + type ReportedId = ReportedId; } /// The `Owned` is a [`"create" strategy`](CreateStrategy). diff --git a/substrate/frame/uniques/src/impl_asset_ops/class.rs b/substrate/frame/uniques/src/impl_asset_ops/class.rs index fba000848c07c..961ccdb01d414 100644 --- a/substrate/frame/uniques/src/impl_asset_ops/class.rs +++ b/substrate/frame/uniques/src/impl_asset_ops/class.rs @@ -1,12 +1,11 @@ use crate::{asset_strategies::Attribute, *}; use frame_support::{ - dispatch::DispatchResult, ensure, traits::{ tokens::asset_ops::{ common_asset_kinds::Class, common_strategies::{ - Adminable, AssignId, Bytes, IfOwnedByWithWitness, Ownership, WithOrigin, + Adminable, Bytes, IfOwnedByWithWitness, Ownership, PredefinedId, WithOrigin, WithWitness, }, AssetDefinition, Create, Destroy, InspectMetadata, @@ -56,11 +55,14 @@ impl<'a, T: Config, I: 'static> InspectMetadata>> } } -impl, I: 'static> Create>> +impl, I: 'static> Create>> for Pallet { - fn create(strategy: Adminable>) -> DispatchResult { - let Adminable { owner, admin, id_assignment: AssignId(collection), .. } = strategy; + fn create( + strategy: Adminable>, + ) -> Result { + let Adminable { owner, admin, id_assignment, .. } = strategy; + let collection = id_assignment.params; Self::do_create_collection( collection.clone(), @@ -68,21 +70,29 @@ impl, I: 'static> Create, I: 'static> - Create>>> - for Pallet + Create< + Class, + WithOrigin>>, + > for Pallet { fn create( - strategy: WithOrigin>>, - ) -> DispatchResult { + strategy: WithOrigin< + T::RuntimeOrigin, + Adminable>, + >, + ) -> Result { let WithOrigin(origin, creation) = strategy; - let Adminable { owner, id_assignment: AssignId(collection), .. } = &creation; + let Adminable { owner, id_assignment, .. } = &creation; + let collection = &id_assignment.params; let maybe_check_signer = T::ForceOrigin::try_origin(origin).map(|_| None).or_else(|origin| { diff --git a/substrate/frame/uniques/src/impl_asset_ops/instance.rs b/substrate/frame/uniques/src/impl_asset_ops/instance.rs index 9a41f9685eced..25ce92859b838 100644 --- a/substrate/frame/uniques/src/impl_asset_ops/instance.rs +++ b/substrate/frame/uniques/src/impl_asset_ops/instance.rs @@ -5,7 +5,8 @@ use frame_support::{ traits::tokens::asset_ops::{ common_asset_kinds::Instance, common_strategies::{ - AssignId, Bytes, CanTransfer, FromTo, IfOwnedBy, JustDo, Owned, Ownership, WithOrigin, + Bytes, CanTransfer, FromTo, IfOwnedBy, JustDo, Owned, Ownership, PredefinedId, + WithOrigin, }, AssetDefinition, Create, Destroy, InspectMetadata, Transfer, }, @@ -70,38 +71,46 @@ impl, I: 'static> InspectMetadata for Pallet } impl, I: 'static> - Create>> for Pallet + Create>> for Pallet { fn create( - strategy: Owned>, - ) -> DispatchResult { - let Owned { owner, id_assignment: AssignId((collection, item)), .. } = strategy; + strategy: Owned>, + ) -> Result<(T::CollectionId, T::ItemId), DispatchError> { + let Owned { owner, id_assignment, .. } = strategy; + let (collection, item) = id_assignment.params; + + Self::do_mint(collection.clone(), item.clone(), owner, |_| Ok(()))?; - Self::do_mint(collection, item, owner, |_| Ok(())) + Ok((collection, item)) } } impl, I: 'static> Create< Instance, - WithOrigin>>, + WithOrigin< + T::RuntimeOrigin, + Owned>, + >, > for Pallet { fn create( strategy: WithOrigin< T::RuntimeOrigin, - Owned>, + Owned>, >, - ) -> DispatchResult { - let WithOrigin(origin, Owned { owner, id_assignment: AssignId((collection, item)), .. }) = - strategy; + ) -> Result<(T::CollectionId, T::ItemId), DispatchError> { + let WithOrigin(origin, Owned { owner, id_assignment, .. }) = strategy; + let (collection, item) = id_assignment.params; let signer = ensure_signed(origin)?; - Self::do_mint(collection, item, owner, |collection_details| { + Self::do_mint(collection.clone(), item.clone(), owner, |collection_details| { ensure!(collection_details.issuer == signer, Error::::NoPermission); Ok(()) - }) + })?; + + Ok((collection, item)) } } From a5b1b1c41fbf64c4c548235aba2636192cfe00b5 Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Tue, 25 Jun 2024 08:29:07 +0200 Subject: [PATCH 13/41] fix: remove unneeded imports --- .../src/unique_instances/derivatives.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs b/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs index cc93183cfc747..4d6450d1c5c31 100644 --- a/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs +++ b/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs @@ -3,16 +3,10 @@ use core::marker::PhantomData; use frame_support::{ ensure, - traits::{ - tokens::asset_ops::{ - common_asset_kinds::{Class, Instance}, - common_strategies::{ - AutoId, DeriveAndReportId, FromTo, IfOwnedBy, IfRestorable, Owned, - }, - AssetDefinition, Create, CreateStrategy, Destroy, DestroyStrategy, Restore, Stash, - Transfer, TransferStrategy, - }, - TypedGet, + traits::tokens::asset_ops::{ + common_asset_kinds::{Class, Instance}, + common_strategies::{AutoId, DeriveAndReportId, FromTo, IfOwnedBy, IfRestorable, Owned}, + AssetDefinition, Create, Destroy, }, }; use sp_runtime::{DispatchError, DispatchResult}; From be99d8e0748adcb8ce44acd1e509ecb8687c2441 Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Tue, 25 Jun 2024 08:29:31 +0200 Subject: [PATCH 14/41] feat: add pallet-xnft --- Cargo.lock | 19 ++ Cargo.toml | 1 + polkadot/xcm/pallet-xnft/Cargo.toml | 62 ++++++ polkadot/xcm/pallet-xnft/README.md | 1 + polkadot/xcm/pallet-xnft/src/lib.rs | 287 ++++++++++++++++++++++++++++ 5 files changed, 370 insertions(+) create mode 100644 polkadot/xcm/pallet-xnft/Cargo.toml create mode 100644 polkadot/xcm/pallet-xnft/README.md create mode 100644 polkadot/xcm/pallet-xnft/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 2a4b9b138bf85..99f47ce0eda63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11723,6 +11723,25 @@ dependencies = [ "staging-xcm-builder", ] +[[package]] +name = "pallet-xnft" +version = "1.0.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 14.0.0", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", +] + [[package]] name = "parachain-template-node" version = "0.0.0" diff --git a/Cargo.toml b/Cargo.toml index 1d02b701d2316..0aacfea2af7cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -221,6 +221,7 @@ members = [ "polkadot/xcm/docs", "polkadot/xcm/pallet-xcm", "polkadot/xcm/pallet-xcm-benchmarks", + "polkadot/xcm/pallet-xnft", "polkadot/xcm/procedural", "polkadot/xcm/xcm-builder", "polkadot/xcm/xcm-executor", diff --git a/polkadot/xcm/pallet-xnft/Cargo.toml b/polkadot/xcm/pallet-xnft/Cargo.toml new file mode 100644 index 0000000000000..101b930edc6b8 --- /dev/null +++ b/polkadot/xcm/pallet-xnft/Cargo.toml @@ -0,0 +1,62 @@ +[package] +name = "pallet-xnft" +version = "1.0.0" +authors.workspace = true +edition.workspace = true +license = "Apache-2.0" +homepage = "https://substrate.io" +repository.workspace = true +description = "FRAME XNFT pallet" +readme = "README.md" + +[lints] +workspace = true + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.6.12", default-features = false } +log = { workspace = true } +scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } +frame-benchmarking = { path = "../../../substrate/frame/benchmarking", default-features = false, optional = true } +frame-support = { path = "../../../substrate/frame/support", default-features = false } +frame-system = { path = "../../../substrate/frame/system", default-features = false } +sp-core = { path = "../../../substrate/primitives/core", default-features = false } +sp-io = { path = "../../../substrate/primitives/io", default-features = false } +sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false } +sp-std = { path = "../../../substrate/primitives/std", default-features = false } +xcm = { package = "staging-xcm", path = "..", default-features = false } +xcm-builder = { package = "staging-xcm-builder", path = "../xcm-builder", default-features = false } +xcm-executor = { package = "staging-xcm-executor", path = "../xcm-executor", default-features = false } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "log/std", + "scale-info/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", + "xcm/std", + "xcm-builder/std", + "xcm-executor/std", +] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "xcm-builder/runtime-benchmarks", + "xcm-executor/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/polkadot/xcm/pallet-xnft/README.md b/polkadot/xcm/pallet-xnft/README.md new file mode 100644 index 0000000000000..a0bada41967f1 --- /dev/null +++ b/polkadot/xcm/pallet-xnft/README.md @@ -0,0 +1 @@ +# xnft diff --git a/polkadot/xcm/pallet-xnft/src/lib.rs b/polkadot/xcm/pallet-xnft/src/lib.rs new file mode 100644 index 0000000000000..ead4bab95f23f --- /dev/null +++ b/polkadot/xcm/pallet-xnft/src/lib.rs @@ -0,0 +1,287 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![recursion_limit = "256"] +// Ensure we're `no_std` when compiling for Wasm. +#![cfg_attr(not(feature = "std"), no_std)] + +use frame_support::{ + pallet_prelude::*, + traits::{ + tokens::asset_ops::{ + common_asset_kinds::Instance, + common_strategies::{DeriveAndReportId, Owned, PredefinedId}, + AssetDefinition, Create, + }, + Incrementable, + }, +}; +use sp_runtime::DispatchResult; +use sp_std::prelude::*; +use xcm::latest::prelude::*; +use xcm_builder::unique_instances::{derivatives::*, NonFungibleAsset}; + +pub use pallet::*; + +/// The log target of this pallet. +pub const LOG_TARGET: &'static str = "runtime::xnft"; + +type DerivativeIdParamsOf = >::DerivativeIdParams; + +type DerivativeIdOf = >::DerivativeId; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + /// The in-code storage version. + const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); + + #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] + pub struct Pallet(PhantomData<(T, I)>); + + #[pallet::config] + /// The module configuration trait. + pub trait Config: frame_system::Config { + /// The overarching event type. + type RuntimeEvent: From> + + IsType<::RuntimeEvent>; + + type DerivativeIdParams: Member + Parameter + MaxEncodedLen; + type DerivativeId: Member + Parameter + MaxEncodedLen; + } + + #[pallet::storage] + #[pallet::getter(fn foreign_asset_to_derivative_id_params)] + pub type ForeignAssetToDerivativeIdParams, I: 'static = ()> = + StorageMap<_, Blake2_128, AssetId, DerivativeIdParamsOf, OptionQuery>; + + #[pallet::storage] + #[pallet::getter(fn derivative_id_params_to_foreign_asset)] + pub type DerivativeIdParamsToForeignAsset, I: 'static = ()> = + StorageMap<_, Blake2_128, DerivativeIdParamsOf, AssetId, OptionQuery>; + + #[pallet::storage] + #[pallet::getter(fn foreign_nft_to_derivative_id)] + pub type ForeignNftToDerivativeId, I: 'static = ()> = StorageDoubleMap< + _, + Blake2_128, + AssetId, + Blake2_128, + AssetInstance, + DerivativeIdOf, + OptionQuery, + >; + + #[pallet::storage] + #[pallet::getter(fn derivative_id_to_foreign_nft)] + pub type DerivativeIdToForeignNft, I: 'static = ()> = + StorageMap<_, Blake2_128, DerivativeIdOf, NonFungibleAsset, OptionQuery>; + + #[pallet::storage] + pub type NextComposableInstanceIdSuffix, I: 'static = ()> + where + T::DerivativeId: CompositeDerivativeId, + = StorageMap< + _, + Blake2_128, + T::DerivativeIdParams, + ::Suffix, + OptionQuery, + >; + + #[pallet::event] + #[pallet::generate_deposit(pub(crate) fn deposit_event)] + pub enum Event, I: 'static = ()> { + /// A derivative instance is registered + /// and bound to a certain foreign nonfungible asset instance. + DerivativeInstanceRegistered { + /// The ID of the derivative instance. + derivative_id: T::DerivativeId, + + /// The XCM ID of the bound foreign nonfungible asset instance. + foreign_nonfungible: NonFungibleAsset, + }, + + /// A derivative instance is de-registered. + DerivativeInstanceDeregistered { + /// The ID of the derivative instance. + derivative_id: T::DerivativeId, + + /// The XCM ID of the bound foreign nonfungible asset instance. + foreign_nonfungible: NonFungibleAsset, + }, + } + + #[pallet::error] + pub enum Error { + /// Can't perform an operation due to the invalid state of storage. + InvalidState, + + /// Unable to set the next instance ID suffix. + CantSetNextInstanceIdSuffix, + } + + #[pallet::call] + impl, I: 'static> Pallet {} +} + +pub struct DerivativeIdParamsRegistry(PhantomData); +impl, I: 'static> DerivativesRegistry + for DerivativeIdParamsRegistry> +{ + fn try_register_derivative( + foreign_asset_id: &AssetId, + derivative_id_params: &T::DerivativeIdParams, + ) -> DispatchResult { + >::insert(foreign_asset_id, derivative_id_params); + >::insert(derivative_id_params, foreign_asset_id); + + Ok(()) + } + + fn try_deregister_derivative(derivative_id_params: &T::DerivativeIdParams) -> DispatchResult { + let foreign_asset_id = + >::take(&derivative_id_params) + .ok_or(Error::::InvalidState)?; + + >::remove(&foreign_asset_id); + + Ok(()) + } + + fn get_derivative(original: &AssetId) -> Option { + >::get(original) + } + + fn get_original(derivative_id_params: &T::DerivativeIdParams) -> Option { + >::get(derivative_id_params) + } +} + +pub struct DerivativeInstancesRegistry(PhantomData); +impl, I: 'static> DerivativesRegistry + for DerivativeInstancesRegistry> +{ + fn try_register_derivative( + foreign_nonfungible @ (original_asset_id, original_asset_instance): &NonFungibleAsset, + derivative_id: &T::DerivativeId, + ) -> DispatchResult { + >::insert( + original_asset_id, + original_asset_instance, + derivative_id, + ); + >::insert(derivative_id, foreign_nonfungible); + + >::deposit_event(Event::::DerivativeInstanceRegistered { + derivative_id: derivative_id.clone(), + foreign_nonfungible: foreign_nonfungible.clone(), + }); + + Ok(()) + } + + fn try_deregister_derivative(derivative_id: &T::DerivativeId) -> DispatchResult { + let foreign_nonfungible = >::take(&derivative_id) + .ok_or(Error::::InvalidState)?; + + >::remove(&foreign_nonfungible.0, &foreign_nonfungible.1); + + >::deposit_event(Event::::DerivativeInstanceDeregistered { + derivative_id: derivative_id.clone(), + foreign_nonfungible, + }); + + Ok(()) + } + + fn get_derivative((asset_id, asset_instance): &NonFungibleAsset) -> Option { + >::get(asset_id, asset_instance) + } + + fn get_original(derivative: &T::DerivativeId) -> Option { + >::get(derivative) + } +} + +pub trait CompositeDerivativeId { + type Prefix: Member + Parameter + MaxEncodedLen; + type Suffix: Member + Parameter + MaxEncodedLen; + + fn compose(prefix: Self::Prefix, suffix: Self::Suffix) -> Self; +} + +impl CompositeDerivativeId for (Prefix, Suffix) +where + Prefix: Member + Parameter + MaxEncodedLen, + Suffix: Member + Parameter + MaxEncodedLen, +{ + type Prefix = Prefix; + type Suffix = Suffix; + + fn compose(prefix: Self::Prefix, suffix: Self::Suffix) -> Self { + (prefix, suffix) + } +} + +pub struct ConcatIncrementableIdOnCreate(PhantomData<(XnftPallet, CreateOp)>); +impl AssetDefinition + for ConcatIncrementableIdOnCreate +where + CreateOp: AssetDefinition, +{ + type Id = CreateOp::Id; +} +impl + Create>> + for ConcatIncrementableIdOnCreate, CreateOp> +where + T: Config, + I: 'static, + T::DerivativeId: CompositeDerivativeId, + ::Suffix: Incrementable, + CreateOp: Create>>, +{ + fn create( + strategy: Owned>, + ) -> Result { + let Owned { owner, id_assignment, .. } = strategy; + let derivative_id_params = id_assignment.params; + + let instance_id_suffix = >::get(&derivative_id_params) + .or(::Suffix::initial_value()) + .ok_or(>::CantSetNextInstanceIdSuffix)?; + + let next_instance_id_suffix = instance_id_suffix + .increment() + .ok_or(>::CantSetNextInstanceIdSuffix)?; + + let derivative_id = + T::DerivativeId::compose(derivative_id_params.clone(), instance_id_suffix); + + CreateOp::create(Owned::new(owner, PredefinedId::from(derivative_id.clone())))?; + + >::insert( + derivative_id_params, + next_instance_id_suffix, + ); + + Ok(derivative_id) + } +} From 6e06663e1f48f75b30aebb0133b2ac30c285578d Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Thu, 27 Jun 2024 09:12:06 +0200 Subject: [PATCH 15/41] fix(xcm-builder): remove unneeded uses, add mod doc comment --- polkadot/xcm/xcm-builder/src/unique_instances/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/mod.rs b/polkadot/xcm/xcm-builder/src/unique_instances/mod.rs index 6f24cd5581d11..8e44b0a519ec3 100644 --- a/polkadot/xcm/xcm-builder/src/unique_instances/mod.rs +++ b/polkadot/xcm/xcm-builder/src/unique_instances/mod.rs @@ -1,5 +1,6 @@ -use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; -use scale_info::TypeInfo; +//! XCM utilities to work with NFT-like entities (unique instances). +//! The adapters and other utility types use the [`asset_ops`](frame_support::traits::tokens::asset_ops) traits. + use xcm::latest::prelude::*; pub mod adapter; From aa2f827ebe49e472cfe342defc76ab924a62cf2c Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Thu, 27 Jun 2024 09:13:15 +0200 Subject: [PATCH 16/41] fix(asset-ops): doc comment --- substrate/frame/support/src/traits/tokens/asset_ops.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/substrate/frame/support/src/traits/tokens/asset_ops.rs b/substrate/frame/support/src/traits/tokens/asset_ops.rs index c4d8863bfcae1..971abd9e4b56b 100644 --- a/substrate/frame/support/src/traits/tokens/asset_ops.rs +++ b/substrate/frame/support/src/traits/tokens/asset_ops.rs @@ -6,6 +6,8 @@ //! * [`Create`] //! * [`Transfer`] //! * [`Destroy`] +//! * [`Stash`] +//! * [`Restore`] //! //! Also, all the operations above (except the `Create` operation) use //! the [`AssetDefinition`] to retrieve the `Id` type of the asset. From 79cae0ae769137096f8006782393b900df163399 Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Thu, 27 Jun 2024 14:05:08 +0200 Subject: [PATCH 17/41] fix: add pallet-xnft to workspace --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index c6b538a2b435d..d5c3a2aefbc3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -972,6 +972,7 @@ pallet-xcm = { path = "polkadot/xcm/pallet-xcm", default-features = false } pallet-xcm-benchmarks = { path = "polkadot/xcm/pallet-xcm-benchmarks", default-features = false } pallet-xcm-bridge-hub = { path = "bridges/modules/xcm-bridge-hub", default-features = false } pallet-xcm-bridge-hub-router = { path = "bridges/modules/xcm-bridge-hub-router", default-features = false } +pallet-xnft = { path = "polkadot/xcm/pallet-xnft", default-features = false } parachain-info = { path = "cumulus/parachains/pallets/parachain-info", default-features = false, package = "staging-parachain-info" } parachain-template-runtime = { path = "templates/parachain/runtime" } parachains-common = { path = "cumulus/parachains/common", default-features = false } From 4c7a0ca9af179d37d5f78b8636547dc7ed6eb5d8 Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Thu, 27 Jun 2024 14:06:36 +0200 Subject: [PATCH 18/41] fix: add Success type to all strategies --- .../xcm-builder/src/unique_instances/ops.rs | 2 +- .../support/src/traits/tokens/asset_ops.rs | 72 ++++++++++++++----- 2 files changed, 55 insertions(+), 19 deletions(-) diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/ops.rs b/polkadot/xcm/xcm-builder/src/unique_instances/ops.rs index 6eb9443b5ca5f..c88bfd5240bda 100644 --- a/polkadot/xcm/xcm-builder/src/unique_instances/ops.rs +++ b/polkadot/xcm/xcm-builder/src/unique_instances/ops.rs @@ -47,7 +47,7 @@ where TransferOp: Transfer, DestroyOp: AssetDefinition, { - fn transfer(id: &Self::Id, strategy: Strategy) -> DispatchResult { + fn transfer(id: &Self::Id, strategy: Strategy) -> Result { TransferOp::transfer(id, strategy) } } diff --git a/substrate/frame/support/src/traits/tokens/asset_ops.rs b/substrate/frame/support/src/traits/tokens/asset_ops.rs index 971abd9e4b56b..631b694081766 100644 --- a/substrate/frame/support/src/traits/tokens/asset_ops.rs +++ b/substrate/frame/support/src/traits/tokens/asset_ops.rs @@ -89,6 +89,10 @@ pub trait InspectMetadata: pub trait MetadataUpdateStrategy { /// The type of metadata update to accept in the [`UpdateMetadata::update_metadata`] function. type Update<'u>; + + /// This type represents a successful asset metadata update. + /// It will be in the [`Result`] type of the [`UpdateMetadata::update_metadata`] function. + type Success; } /// A trait representing the ability of a certain asset kind to **update** its metadata information. @@ -109,7 +113,7 @@ pub trait UpdateMetadata: id: &Self::Id, strategy: Strategy, update: Strategy::Update<'_>, - ) -> DispatchResult; + ) -> Result; } /// A strategy for use in the [`Create`] implementations. @@ -118,8 +122,8 @@ pub trait UpdateMetadata: /// * [`Owned`](common_strategies::Owned) /// * [`Adminable`](common_strategies::Adminable) pub trait CreateStrategy { - /// This type represents successful asset creation. - /// It will be the return type of the [`Create::create`] function. + /// This type represents a successful asset creation. + /// It will be in the [`Result`] type of the [`Create::create`] function. type Success; } @@ -157,7 +161,11 @@ pub trait Create { /// The common transfer strategies are: /// * [`JustDo`](common_strategies::JustDo) /// * [`FromTo`](common_strategies::FromTo) -pub trait TransferStrategy {} +pub trait TransferStrategy { + /// This type represents a successful asset transfer. + /// It will be in the [`Result`] type of the [`Transfer::transfer`] function. + type Success; +} /// A trait representing the ability of a certain asset kind to be transferred. /// @@ -169,7 +177,7 @@ pub trait Transfer: AssetDefinition DispatchResult; + fn transfer(id: &Self::Id, strategy: Strategy) -> Result; } /// A strategy for use in the [`Destroy`] implementations. @@ -180,8 +188,8 @@ pub trait Transfer: AssetDefinition: AssetDefinition: AssetDefinition /// Stash the asset identified by the given `id` using the provided `strategy`. /// /// The ID type is retrieved from the [`AssetDefinition`]. - fn stash(id: &Self::Id, strategy: Strategy) -> DispatchResult; + fn stash(id: &Self::Id, strategy: Strategy) -> Result; } /// A strategy for use in the [`Restore`] implementations. /// The common restore strategies are: /// * [`JustDo`](common_strategies::JustDo) /// * [`IfRestorable`](common_strategies::IfRestorable) -pub trait RestoreStrategy {} +pub trait RestoreStrategy { + /// This type represents a successful asset restoration. + /// It will be in the [`Result`] type of the [`Restore::restore`] function. + type Success; +} /// A trait representing the ability of a certain asset kind to be restored. /// @@ -234,7 +250,7 @@ pub trait Restore: AssetDefinition DispatchResult; + fn restore(id: &Self::Id, strategy: Strategy) -> Result; } /// This modules contains the common asset kinds. @@ -274,11 +290,14 @@ pub mod common_strategies { for WithOrigin { type Update<'u> = Inner::Update<'u>; + type Success = Inner::Success; } impl CreateStrategy for WithOrigin { type Success = Inner::Success; } - impl TransferStrategy for WithOrigin {} + impl TransferStrategy for WithOrigin { + type Success = Inner::Success; + } impl DestroyStrategy for WithOrigin { type Success = Inner::Success; } @@ -301,12 +320,18 @@ pub mod common_strategies { Self(()) } } - impl TransferStrategy for JustDo {} + impl TransferStrategy for JustDo { + type Success = (); + } impl DestroyStrategy for JustDo { type Success = (); } - impl StashStrategy for JustDo {} - impl RestoreStrategy for JustDo {} + impl StashStrategy for JustDo { + type Success = (); + } + impl RestoreStrategy for JustDo { + type Success = (); + } /// The `Bytes` strategy represents raw metadata bytes. /// It is both an [inspect](MetadataInspectStrategy) and [update](MetadataUpdateStrategy) @@ -330,6 +355,7 @@ pub mod common_strategies { } impl MetadataUpdateStrategy for Bytes { type Update<'u> = Option<&'u [u8]>; + type Success = (); } /// The `Ownership` [inspect](MetadataInspectStrategy) metadata strategy allows getting the @@ -366,6 +392,7 @@ pub mod common_strategies { } impl MetadataUpdateStrategy for CanCreate { type Update<'u> = bool; + type Success = (); } /// The `CanTransfer` strategy represents the ability to transfer an asset. @@ -390,6 +417,7 @@ pub mod common_strategies { } impl MetadataUpdateStrategy for CanTransfer { type Update<'u> = bool; + type Success = (); } /// The `CanDestroy` strategy represents the ability to destroy an asset. @@ -414,6 +442,7 @@ pub mod common_strategies { } impl MetadataUpdateStrategy for CanDestroy { type Update<'u> = bool; + type Success = (); } /// The `CanUpdateMetadata` strategy represents the ability to update the metadata of an asset. @@ -439,6 +468,7 @@ pub mod common_strategies { } impl MetadataUpdateStrategy for CanUpdateMetadata { type Update<'u> = bool; + type Success = (); } /// The `AutoId` is an ID assignment approach intended to be used in @@ -561,7 +591,9 @@ pub mod common_strategies { /// /// It accepts two parameters: `from` and `to` whom the asset should be transferred. pub struct FromTo(pub Owner, pub Owner); - impl TransferStrategy for FromTo {} + impl TransferStrategy for FromTo { + type Success = (); + } /// The `IfOwnedBy` is both a [`destroy strategy`](DestroyStrategy) /// and a [`stash strategy`](StashStrategy). @@ -572,7 +604,9 @@ pub mod common_strategies { impl DestroyStrategy for IfOwnedBy { type Success = (); } - impl StashStrategy for IfOwnedBy {} + impl StashStrategy for IfOwnedBy { + type Success = (); + } /// The `IfRestorable` is a [`restore strategy`](RestoreStrategy). /// @@ -581,7 +615,9 @@ pub mod common_strategies { /// this strategy may reference a beneficiary account, /// which should own the asset upon restoration. pub struct IfRestorable(pub Params); - impl RestoreStrategy for IfRestorable {} + impl RestoreStrategy for IfRestorable { + type Success = (); + } /// The `WithWitness` is a [`destroy strategy`](DestroyStrategy). /// From 45855287b8647f34a4b3015facc714232c2ebe3e Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Thu, 27 Jun 2024 14:06:47 +0200 Subject: [PATCH 19/41] chore: cargo fmt --- polkadot/xcm/xcm-builder/src/unique_instances/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/mod.rs b/polkadot/xcm/xcm-builder/src/unique_instances/mod.rs index 8e44b0a519ec3..39e8c66bfb0fb 100644 --- a/polkadot/xcm/xcm-builder/src/unique_instances/mod.rs +++ b/polkadot/xcm/xcm-builder/src/unique_instances/mod.rs @@ -1,5 +1,6 @@ //! XCM utilities to work with NFT-like entities (unique instances). -//! The adapters and other utility types use the [`asset_ops`](frame_support::traits::tokens::asset_ops) traits. +//! The adapters and other utility types use the +//! [`asset_ops`](frame_support::traits::tokens::asset_ops) traits. use xcm::latest::prelude::*; From 5324c5b0286ad1642fa1bcb8eff4113b62a2f2b8 Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Thu, 27 Jun 2024 15:55:41 +0200 Subject: [PATCH 20/41] fix: impl StashStrategy,RestoreStrategy for WithOrigin --- substrate/frame/support/src/traits/tokens/asset_ops.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/substrate/frame/support/src/traits/tokens/asset_ops.rs b/substrate/frame/support/src/traits/tokens/asset_ops.rs index 631b694081766..cca4620dfda80 100644 --- a/substrate/frame/support/src/traits/tokens/asset_ops.rs +++ b/substrate/frame/support/src/traits/tokens/asset_ops.rs @@ -301,6 +301,12 @@ pub mod common_strategies { impl DestroyStrategy for WithOrigin { type Success = Inner::Success; } + impl StashStrategy for WithOrigin { + type Success = Inner::Success; + } + impl RestoreStrategy for WithOrigin { + type Success = Inner::Success; + } /// The JustDo represents the simplest strategy, /// which doesn't require additional checks to perform the operation. From 8f94c1d2f64f3aac199edcc41914d7350d26bcad Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Wed, 3 Jul 2024 14:18:32 +0200 Subject: [PATCH 21/41] fix: CI errors --- Cargo.lock | 1 + polkadot/xcm/pallet-xnft/Cargo.toml | 23 ++++++++++++----------- umbrella/Cargo.toml | 10 +++++++++- umbrella/src/lib.rs | 4 ++++ 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 845e71786b14f..c7a08e40648a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14414,6 +14414,7 @@ dependencies = [ "pallet-xcm-benchmarks", "pallet-xcm-bridge-hub", "pallet-xcm-bridge-hub-router", + "pallet-xnft", "parachains-common", "parachains-runtimes-test-utils", "polkadot-approval-distribution", diff --git a/polkadot/xcm/pallet-xnft/Cargo.toml b/polkadot/xcm/pallet-xnft/Cargo.toml index 101b930edc6b8..13fe163515478 100644 --- a/polkadot/xcm/pallet-xnft/Cargo.toml +++ b/polkadot/xcm/pallet-xnft/Cargo.toml @@ -19,16 +19,17 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "3.6.12", default-features = false } log = { workspace = true } scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } -frame-benchmarking = { path = "../../../substrate/frame/benchmarking", default-features = false, optional = true } -frame-support = { path = "../../../substrate/frame/support", default-features = false } -frame-system = { path = "../../../substrate/frame/system", default-features = false } -sp-core = { path = "../../../substrate/primitives/core", default-features = false } -sp-io = { path = "../../../substrate/primitives/io", default-features = false } -sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false } -sp-std = { path = "../../../substrate/primitives/std", default-features = false } -xcm = { package = "staging-xcm", path = "..", default-features = false } -xcm-builder = { package = "staging-xcm-builder", path = "../xcm-builder", default-features = false } -xcm-executor = { package = "staging-xcm-executor", path = "../xcm-executor", default-features = false } +frame-benchmarking = { optional = true, workspace = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +xcm = { workspace = true } +xcm-executor = { workspace = true } +xcm-builder = { workspace = true } [features] default = ["std"] @@ -43,9 +44,9 @@ std = [ "sp-io/std", "sp-runtime/std", "sp-std/std", - "xcm/std", "xcm-builder/std", "xcm-executor/std", + "xcm/std", ] runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", diff --git a/umbrella/Cargo.toml b/umbrella/Cargo.toml index 94ba09421d409..e3fb561988f18 100644 --- a/umbrella/Cargo.toml +++ b/umbrella/Cargo.toml @@ -162,6 +162,7 @@ std = [ "pallet-xcm-bridge-hub-router?/std", "pallet-xcm-bridge-hub?/std", "pallet-xcm?/std", + "pallet-xnft?/std", "parachains-common?/std", "parachains-runtimes-test-utils?/std", "polkadot-core-primitives?/std", @@ -339,6 +340,7 @@ runtime-benchmarks = [ "pallet-xcm-bridge-hub-router?/runtime-benchmarks", "pallet-xcm-bridge-hub?/runtime-benchmarks", "pallet-xcm?/runtime-benchmarks", + "pallet-xnft?/runtime-benchmarks", "parachains-common?/runtime-benchmarks", "polkadot-cli?/runtime-benchmarks", "polkadot-node-metrics?/runtime-benchmarks", @@ -470,6 +472,7 @@ try-runtime = [ "pallet-xcm-bridge-hub-router?/try-runtime", "pallet-xcm-bridge-hub?/try-runtime", "pallet-xcm?/try-runtime", + "pallet-xnft?/try-runtime", "polkadot-cli?/try-runtime", "polkadot-runtime-common?/try-runtime", "polkadot-runtime-parachains?/try-runtime", @@ -539,7 +542,7 @@ with-tracing = [ "sp-tracing?/with-tracing", "sp-tracing?/with-tracing", ] -runtime = ["assets-common", "binary-merkle-tree", "bp-asset-hub-rococo", "bp-asset-hub-westend", "bp-bridge-hub-cumulus", "bp-bridge-hub-kusama", "bp-bridge-hub-polkadot", "bp-bridge-hub-rococo", "bp-bridge-hub-westend", "bp-header-chain", "bp-kusama", "bp-messages", "bp-parachains", "bp-polkadot", "bp-polkadot-bulletin", "bp-polkadot-core", "bp-relayers", "bp-rococo", "bp-runtime", "bp-test-utils", "bp-westend", "bp-xcm-bridge-hub", "bp-xcm-bridge-hub-router", "bridge-hub-common", "bridge-runtime-common", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-pallet-parachain-system-proc-macro", "cumulus-pallet-session-benchmarking", "cumulus-pallet-solo-to-para", "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", "cumulus-ping", "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-parachain-inherent", "cumulus-primitives-proof-size-hostfunction", "cumulus-primitives-storage-weight-reclaim", "cumulus-primitives-timestamp", "cumulus-primitives-utility", "frame-benchmarking", "frame-benchmarking-pallet-pov", "frame-election-provider-solution-type", "frame-election-provider-support", "frame-executive", "frame-metadata-hash-extension", "frame-support", "frame-support-procedural", "frame-support-procedural-tools-derive", "frame-system", "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", "pallet-alliance", "pallet-asset-conversion", "pallet-asset-conversion-ops", "pallet-asset-conversion-tx-payment", "pallet-asset-rate", "pallet-asset-tx-payment", "pallet-assets", "pallet-assets-freezer", "pallet-atomic-swap", "pallet-aura", "pallet-authority-discovery", "pallet-authorship", "pallet-babe", "pallet-bags-list", "pallet-balances", "pallet-beefy", "pallet-beefy-mmr", "pallet-bounties", "pallet-bridge-grandpa", "pallet-bridge-messages", "pallet-bridge-parachains", "pallet-bridge-relayers", "pallet-broker", "pallet-child-bounties", "pallet-collator-selection", "pallet-collective", "pallet-collective-content", "pallet-contracts", "pallet-contracts-proc-macro", "pallet-contracts-uapi", "pallet-conviction-voting", "pallet-core-fellowship", "pallet-delegated-staking", "pallet-democracy", "pallet-dev-mode", "pallet-election-provider-multi-phase", "pallet-election-provider-support-benchmarking", "pallet-elections-phragmen", "pallet-fast-unstake", "pallet-glutton", "pallet-grandpa", "pallet-identity", "pallet-im-online", "pallet-indices", "pallet-insecure-randomness-collective-flip", "pallet-lottery", "pallet-membership", "pallet-message-queue", "pallet-migrations", "pallet-mixnet", "pallet-mmr", "pallet-multisig", "pallet-nft-fractionalization", "pallet-nfts", "pallet-nfts-runtime-api", "pallet-nis", "pallet-node-authorization", "pallet-nomination-pools", "pallet-nomination-pools-benchmarking", "pallet-nomination-pools-runtime-api", "pallet-offences", "pallet-offences-benchmarking", "pallet-paged-list", "pallet-parameters", "pallet-preimage", "pallet-proxy", "pallet-ranked-collective", "pallet-recovery", "pallet-referenda", "pallet-remark", "pallet-root-offences", "pallet-root-testing", "pallet-safe-mode", "pallet-salary", "pallet-scheduler", "pallet-scored-pool", "pallet-session", "pallet-session-benchmarking", "pallet-skip-feeless-payment", "pallet-society", "pallet-staking", "pallet-staking-reward-curve", "pallet-staking-reward-fn", "pallet-staking-runtime-api", "pallet-state-trie-migration", "pallet-statement", "pallet-sudo", "pallet-timestamp", "pallet-tips", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-transaction-storage", "pallet-treasury", "pallet-tx-pause", "pallet-uniques", "pallet-utility", "pallet-vesting", "pallet-whitelist", "pallet-xcm", "pallet-xcm-benchmarks", "pallet-xcm-bridge-hub", "pallet-xcm-bridge-hub-router", "parachains-common", "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-primitives", "polkadot-runtime-common", "polkadot-runtime-metrics", "polkadot-runtime-parachains", "polkadot-sdk-frame", "rococo-runtime-constants", "sc-chain-spec-derive", "sc-tracing-proc-macro", "slot-range-helper", "snowbridge-beacon-primitives", "snowbridge-core", "snowbridge-ethereum", "snowbridge-outbound-queue-merkle-tree", "snowbridge-outbound-queue-runtime-api", "snowbridge-pallet-ethereum-client", "snowbridge-pallet-ethereum-client-fixtures", "snowbridge-pallet-inbound-queue", "snowbridge-pallet-inbound-queue-fixtures", "snowbridge-pallet-outbound-queue", "snowbridge-pallet-system", "snowbridge-router-primitives", "snowbridge-runtime-common", "snowbridge-system-runtime-api", "sp-api", "sp-api-proc-macro", "sp-application-crypto", "sp-arithmetic", "sp-authority-discovery", "sp-block-builder", "sp-consensus-aura", "sp-consensus-babe", "sp-consensus-beefy", "sp-consensus-grandpa", "sp-consensus-pow", "sp-consensus-slots", "sp-core", "sp-crypto-ec-utils", "sp-crypto-hashing", "sp-crypto-hashing-proc-macro", "sp-debug-derive", "sp-externalities", "sp-genesis-builder", "sp-inherents", "sp-io", "sp-keyring", "sp-keystore", "sp-metadata-ir", "sp-mixnet", "sp-mmr-primitives", "sp-npos-elections", "sp-offchain", "sp-runtime", "sp-runtime-interface", "sp-runtime-interface-proc-macro", "sp-session", "sp-staking", "sp-state-machine", "sp-statement-store", "sp-std", "sp-storage", "sp-timestamp", "sp-tracing", "sp-transaction-pool", "sp-transaction-storage-proof", "sp-trie", "sp-version", "sp-version-proc-macro", "sp-wasm-interface", "sp-weights", "staging-parachain-info", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", "substrate-bip39", "testnet-parachains-constants", "tracing-gum-proc-macro", "westend-runtime-constants", "xcm-procedural", "xcm-runtime-apis"] +runtime = ["assets-common", "binary-merkle-tree", "bp-asset-hub-rococo", "bp-asset-hub-westend", "bp-bridge-hub-cumulus", "bp-bridge-hub-kusama", "bp-bridge-hub-polkadot", "bp-bridge-hub-rococo", "bp-bridge-hub-westend", "bp-header-chain", "bp-kusama", "bp-messages", "bp-parachains", "bp-polkadot", "bp-polkadot-bulletin", "bp-polkadot-core", "bp-relayers", "bp-rococo", "bp-runtime", "bp-test-utils", "bp-westend", "bp-xcm-bridge-hub", "bp-xcm-bridge-hub-router", "bridge-hub-common", "bridge-runtime-common", "cumulus-pallet-aura-ext", "cumulus-pallet-dmp-queue", "cumulus-pallet-parachain-system", "cumulus-pallet-parachain-system-proc-macro", "cumulus-pallet-session-benchmarking", "cumulus-pallet-solo-to-para", "cumulus-pallet-xcm", "cumulus-pallet-xcmp-queue", "cumulus-ping", "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-parachain-inherent", "cumulus-primitives-proof-size-hostfunction", "cumulus-primitives-storage-weight-reclaim", "cumulus-primitives-timestamp", "cumulus-primitives-utility", "frame-benchmarking", "frame-benchmarking-pallet-pov", "frame-election-provider-solution-type", "frame-election-provider-support", "frame-executive", "frame-metadata-hash-extension", "frame-support", "frame-support-procedural", "frame-support-procedural-tools-derive", "frame-system", "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", "pallet-alliance", "pallet-asset-conversion", "pallet-asset-conversion-ops", "pallet-asset-conversion-tx-payment", "pallet-asset-rate", "pallet-asset-tx-payment", "pallet-assets", "pallet-assets-freezer", "pallet-atomic-swap", "pallet-aura", "pallet-authority-discovery", "pallet-authorship", "pallet-babe", "pallet-bags-list", "pallet-balances", "pallet-beefy", "pallet-beefy-mmr", "pallet-bounties", "pallet-bridge-grandpa", "pallet-bridge-messages", "pallet-bridge-parachains", "pallet-bridge-relayers", "pallet-broker", "pallet-child-bounties", "pallet-collator-selection", "pallet-collective", "pallet-collective-content", "pallet-contracts", "pallet-contracts-proc-macro", "pallet-contracts-uapi", "pallet-conviction-voting", "pallet-core-fellowship", "pallet-delegated-staking", "pallet-democracy", "pallet-dev-mode", "pallet-election-provider-multi-phase", "pallet-election-provider-support-benchmarking", "pallet-elections-phragmen", "pallet-fast-unstake", "pallet-glutton", "pallet-grandpa", "pallet-identity", "pallet-im-online", "pallet-indices", "pallet-insecure-randomness-collective-flip", "pallet-lottery", "pallet-membership", "pallet-message-queue", "pallet-migrations", "pallet-mixnet", "pallet-mmr", "pallet-multisig", "pallet-nft-fractionalization", "pallet-nfts", "pallet-nfts-runtime-api", "pallet-nis", "pallet-node-authorization", "pallet-nomination-pools", "pallet-nomination-pools-benchmarking", "pallet-nomination-pools-runtime-api", "pallet-offences", "pallet-offences-benchmarking", "pallet-paged-list", "pallet-parameters", "pallet-preimage", "pallet-proxy", "pallet-ranked-collective", "pallet-recovery", "pallet-referenda", "pallet-remark", "pallet-root-offences", "pallet-root-testing", "pallet-safe-mode", "pallet-salary", "pallet-scheduler", "pallet-scored-pool", "pallet-session", "pallet-session-benchmarking", "pallet-skip-feeless-payment", "pallet-society", "pallet-staking", "pallet-staking-reward-curve", "pallet-staking-reward-fn", "pallet-staking-runtime-api", "pallet-state-trie-migration", "pallet-statement", "pallet-sudo", "pallet-timestamp", "pallet-tips", "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-transaction-storage", "pallet-treasury", "pallet-tx-pause", "pallet-uniques", "pallet-utility", "pallet-vesting", "pallet-whitelist", "pallet-xcm", "pallet-xcm-benchmarks", "pallet-xcm-bridge-hub", "pallet-xcm-bridge-hub-router", "pallet-xnft", "parachains-common", "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-primitives", "polkadot-runtime-common", "polkadot-runtime-metrics", "polkadot-runtime-parachains", "polkadot-sdk-frame", "rococo-runtime-constants", "sc-chain-spec-derive", "sc-tracing-proc-macro", "slot-range-helper", "snowbridge-beacon-primitives", "snowbridge-core", "snowbridge-ethereum", "snowbridge-outbound-queue-merkle-tree", "snowbridge-outbound-queue-runtime-api", "snowbridge-pallet-ethereum-client", "snowbridge-pallet-ethereum-client-fixtures", "snowbridge-pallet-inbound-queue", "snowbridge-pallet-inbound-queue-fixtures", "snowbridge-pallet-outbound-queue", "snowbridge-pallet-system", "snowbridge-router-primitives", "snowbridge-runtime-common", "snowbridge-system-runtime-api", "sp-api", "sp-api-proc-macro", "sp-application-crypto", "sp-arithmetic", "sp-authority-discovery", "sp-block-builder", "sp-consensus-aura", "sp-consensus-babe", "sp-consensus-beefy", "sp-consensus-grandpa", "sp-consensus-pow", "sp-consensus-slots", "sp-core", "sp-crypto-ec-utils", "sp-crypto-hashing", "sp-crypto-hashing-proc-macro", "sp-debug-derive", "sp-externalities", "sp-genesis-builder", "sp-inherents", "sp-io", "sp-keyring", "sp-keystore", "sp-metadata-ir", "sp-mixnet", "sp-mmr-primitives", "sp-npos-elections", "sp-offchain", "sp-runtime", "sp-runtime-interface", "sp-runtime-interface-proc-macro", "sp-session", "sp-staking", "sp-state-machine", "sp-statement-store", "sp-std", "sp-storage", "sp-timestamp", "sp-tracing", "sp-transaction-pool", "sp-transaction-storage-proof", "sp-trie", "sp-version", "sp-version-proc-macro", "sp-wasm-interface", "sp-weights", "staging-parachain-info", "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", "substrate-bip39", "testnet-parachains-constants", "tracing-gum-proc-macro", "westend-runtime-constants", "xcm-procedural", "xcm-runtime-apis"] node = ["asset-test-utils", "bridge-hub-test-utils", "cumulus-client-cli", "cumulus-client-collator", "cumulus-client-consensus-aura", "cumulus-client-consensus-common", "cumulus-client-consensus-proposer", "cumulus-client-consensus-relay-chain", "cumulus-client-network", "cumulus-client-parachain-inherent", "cumulus-client-pov-recovery", "cumulus-client-service", "cumulus-relay-chain-inprocess-interface", "cumulus-relay-chain-interface", "cumulus-relay-chain-minimal-node", "cumulus-relay-chain-rpc-interface", "cumulus-test-relay-sproof-builder", "emulated-integration-tests-common", "fork-tree", "frame-benchmarking-cli", "frame-remote-externalities", "frame-support-procedural-tools", "generate-bags", "mmr-gadget", "mmr-rpc", "pallet-contracts-mock-network", "pallet-transaction-payment-rpc", "parachains-runtimes-test-utils", "polkadot-approval-distribution", "polkadot-availability-bitfield-distribution", "polkadot-availability-distribution", "polkadot-availability-recovery", "polkadot-cli", "polkadot-collator-protocol", "polkadot-dispute-distribution", "polkadot-erasure-coding", "polkadot-gossip-support", "polkadot-network-bridge", "polkadot-node-collation-generation", "polkadot-node-core-approval-voting", "polkadot-node-core-av-store", "polkadot-node-core-backing", "polkadot-node-core-bitfield-signing", "polkadot-node-core-candidate-validation", "polkadot-node-core-chain-api", "polkadot-node-core-chain-selection", "polkadot-node-core-dispute-coordinator", "polkadot-node-core-parachains-inherent", "polkadot-node-core-prospective-parachains", "polkadot-node-core-provisioner", "polkadot-node-core-pvf", "polkadot-node-core-pvf-checker", "polkadot-node-core-pvf-common", "polkadot-node-core-pvf-execute-worker", "polkadot-node-core-pvf-prepare-worker", "polkadot-node-core-runtime-api", "polkadot-node-jaeger", "polkadot-node-metrics", "polkadot-node-network-protocol", "polkadot-node-primitives", "polkadot-node-subsystem", "polkadot-node-subsystem-types", "polkadot-node-subsystem-util", "polkadot-overseer", "polkadot-rpc", "polkadot-service", "polkadot-statement-distribution", "polkadot-statement-table", "sc-allocator", "sc-authority-discovery", "sc-basic-authorship", "sc-block-builder", "sc-chain-spec", "sc-cli", "sc-client-api", "sc-client-db", "sc-consensus", "sc-consensus-aura", "sc-consensus-babe", "sc-consensus-babe-rpc", "sc-consensus-beefy", "sc-consensus-beefy-rpc", "sc-consensus-epochs", "sc-consensus-grandpa", "sc-consensus-grandpa-rpc", "sc-consensus-manual-seal", "sc-consensus-pow", "sc-consensus-slots", "sc-executor", "sc-executor-common", "sc-executor-polkavm", "sc-executor-wasmtime", "sc-informant", "sc-keystore", "sc-mixnet", "sc-network", "sc-network-common", "sc-network-gossip", "sc-network-light", "sc-network-statement", "sc-network-sync", "sc-network-transactions", "sc-network-types", "sc-offchain", "sc-proposer-metrics", "sc-rpc", "sc-rpc-api", "sc-rpc-server", "sc-rpc-spec-v2", "sc-service", "sc-state-db", "sc-statement-store", "sc-storage-monitor", "sc-sync-state-rpc", "sc-sysinfo", "sc-telemetry", "sc-tracing", "sc-transaction-pool", "sc-transaction-pool-api", "sc-utils", "snowbridge-runtime-test-common", "sp-blockchain", "sp-consensus", "sp-core-hashing", "sp-core-hashing-proc-macro", "sp-database", "sp-maybe-compressed-blob", "sp-panic-handler", "sp-rpc", "staging-chain-spec-builder", "staging-node-inspect", "staging-tracking-allocator", "std", "subkey", "substrate-build-script-utils", "substrate-frame-rpc-support", "substrate-frame-rpc-system", "substrate-prometheus-endpoint", "substrate-rpc-client", "substrate-state-trie-migration-rpc", "substrate-wasm-builder", "tracing-gum", "xcm-emulator", "xcm-simulator"] tuples-96 = [ "frame-support-procedural?/tuples-96", @@ -1332,6 +1335,11 @@ path = "../bridges/modules/xcm-bridge-hub-router" default-features = false optional = true +[dependencies.pallet-xnft] +path = "../polkadot/xcm/pallet-xnft" +default-features = false +optional = true + [dependencies.parachains-common] path = "../cumulus/parachains/common" default-features = false diff --git a/umbrella/src/lib.rs b/umbrella/src/lib.rs index 58a5691961d9b..0ed7ce8b6acfa 100644 --- a/umbrella/src/lib.rs +++ b/umbrella/src/lib.rs @@ -753,6 +753,10 @@ pub use pallet_xcm_bridge_hub; #[cfg(feature = "pallet-xcm-bridge-hub-router")] pub use pallet_xcm_bridge_hub_router; +/// FRAME XNFT pallet. +#[cfg(feature = "pallet-xnft")] +pub use pallet_xnft; + /// Logic which is common to all parachain runtimes. #[cfg(feature = "parachains-common")] pub use parachains_common; From 23c36beeed949ce2b60a9e1a62583cb3d57d0f58 Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Wed, 3 Jul 2024 15:49:50 +0200 Subject: [PATCH 22/41] fix: remove unused imports --- polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs | 4 ++-- polkadot/xcm/xcm-builder/src/unique_instances/ops.rs | 4 +--- substrate/frame/support/src/traits/tokens/asset_ops.rs | 1 - 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs b/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs index 4d6450d1c5c31..ff0ac523cd149 100644 --- a/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs +++ b/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs @@ -4,8 +4,8 @@ use core::marker::PhantomData; use frame_support::{ ensure, traits::tokens::asset_ops::{ - common_asset_kinds::{Class, Instance}, - common_strategies::{AutoId, DeriveAndReportId, FromTo, IfOwnedBy, IfRestorable, Owned}, + common_asset_kinds::Instance, + common_strategies::{DeriveAndReportId, IfOwnedBy, Owned}, AssetDefinition, Create, Destroy, }, }; diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/ops.rs b/polkadot/xcm/xcm-builder/src/unique_instances/ops.rs index c88bfd5240bda..fe16daed04593 100644 --- a/polkadot/xcm/xcm-builder/src/unique_instances/ops.rs +++ b/polkadot/xcm/xcm-builder/src/unique_instances/ops.rs @@ -4,9 +4,7 @@ use core::marker::PhantomData; use frame_support::traits::{ tokens::asset_ops::{ common_asset_kinds::Instance, - common_strategies::{ - DeriveAndReportId, FromTo, IfOwnedBy, IfRestorable, Owned, PredefinedId, - }, + common_strategies::{FromTo, IfOwnedBy, IfRestorable, Owned, PredefinedId}, AssetDefinition, Create, CreateStrategy, Destroy, DestroyStrategy, Restore, Stash, Transfer, TransferStrategy, }, diff --git a/substrate/frame/support/src/traits/tokens/asset_ops.rs b/substrate/frame/support/src/traits/tokens/asset_ops.rs index cca4620dfda80..9a03a3fdfdeda 100644 --- a/substrate/frame/support/src/traits/tokens/asset_ops.rs +++ b/substrate/frame/support/src/traits/tokens/asset_ops.rs @@ -22,7 +22,6 @@ //! may supply additional parameters, //! and may define a return value type of the operation. -use crate::dispatch::DispatchResult; use core::marker::PhantomData; use sp_runtime::DispatchError; use sp_std::vec::Vec; From 845149ac9d92fb05391d14320a8169d86bf7aa11 Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Wed, 3 Jul 2024 16:08:45 +0200 Subject: [PATCH 23/41] fix: clippy --- substrate/frame/uniques/src/impl_asset_ops/instance.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/frame/uniques/src/impl_asset_ops/instance.rs b/substrate/frame/uniques/src/impl_asset_ops/instance.rs index 25ce92859b838..d3211cf5eb551 100644 --- a/substrate/frame/uniques/src/impl_asset_ops/instance.rs +++ b/substrate/frame/uniques/src/impl_asset_ops/instance.rs @@ -79,7 +79,7 @@ impl, I: 'static> let Owned { owner, id_assignment, .. } = strategy; let (collection, item) = id_assignment.params; - Self::do_mint(collection.clone(), item.clone(), owner, |_| Ok(()))?; + Self::do_mint(collection.clone(), item, owner, |_| Ok(()))?; Ok((collection, item)) } From f7e2c3e3de57eef66e86dff70eda8b88477f8da6 Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Wed, 3 Jul 2024 17:38:41 +0200 Subject: [PATCH 24/41] fix: clippy --- polkadot/xcm/xcm-builder/src/unique_instances/adapter.rs | 1 - substrate/frame/uniques/src/impl_asset_ops/instance.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/adapter.rs b/polkadot/xcm/xcm-builder/src/unique_instances/adapter.rs index 077466357a024..5456e683e2a40 100644 --- a/polkadot/xcm/xcm-builder/src/unique_instances/adapter.rs +++ b/polkadot/xcm/xcm-builder/src/unique_instances/adapter.rs @@ -1,6 +1,5 @@ use core::marker::PhantomData; use frame_support::traits::tokens::asset_ops::{ - self, common_asset_kinds::Instance, common_strategies::{DeriveAndReportId, FromTo, IfOwnedBy, Owned, PredefinedId}, AssetDefinition, Create, Destroy, Transfer, diff --git a/substrate/frame/uniques/src/impl_asset_ops/instance.rs b/substrate/frame/uniques/src/impl_asset_ops/instance.rs index d3211cf5eb551..de7aefb47ea42 100644 --- a/substrate/frame/uniques/src/impl_asset_ops/instance.rs +++ b/substrate/frame/uniques/src/impl_asset_ops/instance.rs @@ -105,7 +105,7 @@ impl, I: 'static> let signer = ensure_signed(origin)?; - Self::do_mint(collection.clone(), item.clone(), owner, |collection_details| { + Self::do_mint(collection.clone(), item, owner, |collection_details| { ensure!(collection_details.issuer == signer, Error::::NoPermission); Ok(()) })?; From d870d91d1b48aff1e74cd81d0b64e8b24d3fb40e Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Mon, 5 Aug 2024 12:12:20 +0200 Subject: [PATCH 25/41] fix: cargo fmt, remove unneeded use --- .../assets/asset-hub-rococo/src/xcm_config.rs | 22 +++++++++---------- .../asset-hub-westend/src/xcm_config.rs | 21 +++++++++--------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs index 607aecd8339de..f8e9f8f5ecf05 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs @@ -49,17 +49,17 @@ use testnet_parachains_constants::rococo::snowbridge::{ }; use xcm::latest::prelude::*; use xcm_builder::{ - unique_instances::UniqueInstancesAdapter, AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, - AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, - DenyReserveTransferToRelayChain, DenyThenTry, DescribeAllTerminal, DescribeFamily, - EnsureXcmOrigin, FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, - GlobalConsensusParachainConvertsFor, HashedDescription, IsConcrete, LocalMint, MatchInClassInstances, - NetworkExportTableItem, NoChecking, NonFungiblesAdapter, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignPaidRemoteExporter, - SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, TakeWeightCredit, - TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, - XcmFeeManagerFromComponents, + unique_instances::UniqueInstancesAdapter, AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, + AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, + AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, DenyThenTry, + DescribeAllTerminal, DescribeFamily, EnsureXcmOrigin, FrameTransactionalProcessor, + FungibleAdapter, FungiblesAdapter, GlobalConsensusParachainConvertsFor, HashedDescription, + IsConcrete, LocalMint, MatchInClassInstances, NetworkExportTableItem, NoChecking, + ParentAsSuperuser, ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, + SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignPaidRemoteExporter, SovereignSignedViaLocation, StartsWith, + StartsWithExplicitGlobalConsensus, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, + WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, }; use xcm_executor::XcmExecutor; diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index d468bda091316..c4a6e3f772f12 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -45,16 +45,17 @@ use polkadot_runtime_common::xcm_sender::ExponentialPrice; use sp_runtime::traits::{AccountIdConversion, ConvertInto}; use xcm::latest::prelude::*; use xcm_builder::{ - unique_instances::UniqueInstancesAdapter, AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, AllowHrmpNotificationsFromRelayChain, - AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, - DenyReserveTransferToRelayChain, DenyThenTry, DescribeFamily, DescribePalletTerminal, - EnsureXcmOrigin, FrameTransactionalProcessor, FungibleAdapter, FungiblesAdapter, - GlobalConsensusParachainConvertsFor, HashedDescription, IsConcrete, LocalMint, MatchInClassInstances, - NetworkExportTableItem, NoChecking, NonFungiblesAdapter, ParentAsSuperuser, ParentIsPreset, - RelayChainAsNative, SendXcmFeeToAccount, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, StartsWith, - StartsWithExplicitGlobalConsensus, TakeWeightCredit, TrailingSetTopicAsId, UsingComponents, - WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, XcmFeeManagerFromComponents, + unique_instances::UniqueInstancesAdapter, AccountId32Aliases, AllowExplicitUnpaidExecutionFrom, + AllowHrmpNotificationsFromRelayChain, AllowKnownQueryResponses, AllowSubscriptionsFrom, + AllowTopLevelPaidExecutionFrom, DenyReserveTransferToRelayChain, DenyThenTry, DescribeFamily, + DescribePalletTerminal, EnsureXcmOrigin, FrameTransactionalProcessor, FungibleAdapter, + FungiblesAdapter, GlobalConsensusParachainConvertsFor, HashedDescription, IsConcrete, + LocalMint, MatchInClassInstances, NetworkExportTableItem, NoChecking, ParentAsSuperuser, + ParentIsPreset, RelayChainAsNative, SendXcmFeeToAccount, SiblingParachainAsNative, + SiblingParachainConvertsVia, SignedAccountId32AsNative, SignedToAccountId32, + SovereignSignedViaLocation, StartsWith, StartsWithExplicitGlobalConsensus, TakeWeightCredit, + TrailingSetTopicAsId, UsingComponents, WeightInfoBounds, WithComputedOrigin, WithUniqueTopic, + XcmFeeManagerFromComponents, }; use xcm_executor::XcmExecutor; From 1a5090401317231974a507d55f432cf16b8d6a3f Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Mon, 5 Aug 2024 12:53:04 +0200 Subject: [PATCH 26/41] fix: umbrella crate --- umbrella/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/umbrella/Cargo.toml b/umbrella/Cargo.toml index 459f616fdc4ae..900628b407cad 100644 --- a/umbrella/Cargo.toml +++ b/umbrella/Cargo.toml @@ -700,6 +700,7 @@ runtime = [ "pallet-xcm-benchmarks", "pallet-xcm-bridge-hub", "pallet-xcm-bridge-hub-router", + "pallet-xnft", "parachains-common", "polkadot-core-primitives", "polkadot-parachain-primitives", From cd24f7688a1200678cb1d531742126f4baec1a00 Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Thu, 19 Sep 2024 15:46:27 +0200 Subject: [PATCH 27/41] refactor: replace pallet-xnft with pallet-derivatives --- Cargo.lock | 40 +-- Cargo.toml | 4 +- .../Cargo.toml | 4 +- polkadot/xcm/pallet-derivatives/README.md | 1 + polkadot/xcm/pallet-derivatives/src/lib.rs | 134 ++++++++ polkadot/xcm/pallet-xnft/README.md | 1 - polkadot/xcm/pallet-xnft/src/lib.rs | 287 ------------------ umbrella/Cargo.toml | 12 +- umbrella/src/lib.rs | 6 +- 9 files changed, 168 insertions(+), 321 deletions(-) rename polkadot/xcm/{pallet-xnft => pallet-derivatives}/Cargo.toml (95%) create mode 100644 polkadot/xcm/pallet-derivatives/README.md create mode 100644 polkadot/xcm/pallet-derivatives/src/lib.rs delete mode 100644 polkadot/xcm/pallet-xnft/README.md delete mode 100644 polkadot/xcm/pallet-xnft/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 3c9b073879ebf..8a7ddfdfd40b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10502,6 +10502,25 @@ dependencies = [ "sp-runtime", ] +[[package]] +name = "pallet-derivatives" +version = "1.0.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 14.0.0", + "staging-xcm", + "staging-xcm-builder", + "staging-xcm-executor", +] + [[package]] name = "pallet-dev-mode" version = "10.0.0" @@ -12022,25 +12041,6 @@ dependencies = [ "staging-xcm-builder", ] -[[package]] -name = "pallet-xnft" -version = "1.0.0" -dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", - "log", - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std 14.0.0", - "staging-xcm", - "staging-xcm-builder", - "staging-xcm-executor", -] - [[package]] name = "parachain-template-node" version = "0.0.0" @@ -14280,6 +14280,7 @@ dependencies = [ "pallet-core-fellowship", "pallet-delegated-staking", "pallet-democracy", + "pallet-derivatives", "pallet-dev-mode", "pallet-election-provider-multi-phase", "pallet-election-provider-support-benchmarking", @@ -14349,7 +14350,6 @@ dependencies = [ "pallet-xcm-benchmarks", "pallet-xcm-bridge-hub", "pallet-xcm-bridge-hub-router", - "pallet-xnft", "parachains-common", "parachains-runtimes-test-utils", "polkadot-approval-distribution", diff --git a/Cargo.toml b/Cargo.toml index ef3971b5e5e1e..f549b4d39ebfc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -227,7 +227,7 @@ members = [ "polkadot/xcm/docs", "polkadot/xcm/pallet-xcm", "polkadot/xcm/pallet-xcm-benchmarks", - "polkadot/xcm/pallet-xnft", + "polkadot/xcm/pallet-derivatives", "polkadot/xcm/procedural", "polkadot/xcm/xcm-builder", "polkadot/xcm/xcm-executor", @@ -984,7 +984,7 @@ pallet-xcm = { path = "polkadot/xcm/pallet-xcm", default-features = false } pallet-xcm-benchmarks = { path = "polkadot/xcm/pallet-xcm-benchmarks", default-features = false } pallet-xcm-bridge-hub = { path = "bridges/modules/xcm-bridge-hub", default-features = false } pallet-xcm-bridge-hub-router = { path = "bridges/modules/xcm-bridge-hub-router", default-features = false } -pallet-xnft = { path = "polkadot/xcm/pallet-xnft", default-features = false } +pallet-derivatives = { path = "polkadot/xcm/pallet-derivatives", default-features = false } parachain-info = { path = "cumulus/parachains/pallets/parachain-info", default-features = false, package = "staging-parachain-info" } parachain-template-runtime = { path = "templates/parachain/runtime" } parachains-common = { path = "cumulus/parachains/common", default-features = false } diff --git a/polkadot/xcm/pallet-xnft/Cargo.toml b/polkadot/xcm/pallet-derivatives/Cargo.toml similarity index 95% rename from polkadot/xcm/pallet-xnft/Cargo.toml rename to polkadot/xcm/pallet-derivatives/Cargo.toml index 13fe163515478..269498f4018cf 100644 --- a/polkadot/xcm/pallet-xnft/Cargo.toml +++ b/polkadot/xcm/pallet-derivatives/Cargo.toml @@ -1,12 +1,12 @@ [package] -name = "pallet-xnft" +name = "pallet-derivatives" version = "1.0.0" authors.workspace = true edition.workspace = true license = "Apache-2.0" homepage = "https://substrate.io" repository.workspace = true -description = "FRAME XNFT pallet" +description = "XCM derivatives pallet" readme = "README.md" [lints] diff --git a/polkadot/xcm/pallet-derivatives/README.md b/polkadot/xcm/pallet-derivatives/README.md new file mode 100644 index 0000000000000..627482d2091af --- /dev/null +++ b/polkadot/xcm/pallet-derivatives/README.md @@ -0,0 +1 @@ +# derivatives diff --git a/polkadot/xcm/pallet-derivatives/src/lib.rs b/polkadot/xcm/pallet-derivatives/src/lib.rs new file mode 100644 index 0000000000000..9c46a7f7439d7 --- /dev/null +++ b/polkadot/xcm/pallet-derivatives/src/lib.rs @@ -0,0 +1,134 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![recursion_limit = "256"] +// Ensure we're `no_std` when compiling for Wasm. +#![cfg_attr(not(feature = "std"), no_std)] + +use frame_support::pallet_prelude::*; +use sp_runtime::DispatchResult; +use xcm_builder::unique_instances::derivatives::DerivativesRegistry; + +pub use pallet::*; + +/// The log target of this pallet. +pub const LOG_TARGET: &'static str = "runtime::xcm::derivatives"; + +type OriginalOf = >::Original; + +type DerivativeOf = >::Derivative; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + /// The in-code storage version. + const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); + + #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] + pub struct Pallet(PhantomData<(T, I)>); + + #[pallet::config] + /// The module configuration trait. + pub trait Config: frame_system::Config { + /// The overarching event type. + type RuntimeEvent: From> + + IsType<::RuntimeEvent>; + + type Original: Member + Parameter + MaxEncodedLen; + type Derivative: Member + Parameter + MaxEncodedLen; + } + + #[pallet::storage] + #[pallet::getter(fn original_to_derivative)] + pub type OriginalToDerivative, I: 'static = ()> = + StorageMap<_, Blake2_128, OriginalOf, DerivativeOf, OptionQuery>; + + #[pallet::storage] + #[pallet::getter(fn derivative_to_original)] + pub type DerivativeToOriginal, I: 'static = ()> = + StorageMap<_, Blake2_128, DerivativeOf, OriginalOf, OptionQuery>; + + #[pallet::event] + #[pallet::generate_deposit(pub(crate) fn deposit_event)] + pub enum Event, I: 'static = ()> { + /// A derivative is registered. + DerivativeRegistered { original: OriginalOf, derivative: DerivativeOf }, + + /// A derivative is de-registered. + DerivativeDeregistered { original: OriginalOf, derivative: DerivativeOf }, + } + + #[pallet::error] + pub enum Error { + /// A derivative already exists. + DerivativeAlreadyExists, + + /// Failed to deregister a non-registered derivative + NoDerivativeToDeregister, + } + + #[pallet::call] + impl, I: 'static> Pallet {} +} + +impl, I: 'static> DerivativesRegistry, DerivativeOf> + for Pallet +{ + fn try_register_derivative( + original: &OriginalOf, + derivative: &DerivativeOf, + ) -> DispatchResult { + ensure!( + Self::original_to_derivative(original).is_none(), + Error::::DerivativeAlreadyExists, + ); + + >::insert(original, derivative); + >::insert(derivative, original); + + Self::deposit_event(Event::::DerivativeRegistered { + original: original.clone(), + derivative: derivative.clone(), + }); + + Ok(()) + } + + fn try_deregister_derivative(derivative: &DerivativeOf) -> DispatchResult { + let original = >::take(&derivative) + .ok_or(Error::::NoDerivativeToDeregister)?; + + >::remove(&original); + + Self::deposit_event(Event::::DerivativeDeregistered { + original: original.clone(), + derivative: derivative.clone(), + }); + + Ok(()) + } + + fn get_derivative(original: &OriginalOf) -> Option> { + >::get(original) + } + + fn get_original(derivative: &DerivativeOf) -> Option> { + >::get(derivative) + } +} diff --git a/polkadot/xcm/pallet-xnft/README.md b/polkadot/xcm/pallet-xnft/README.md deleted file mode 100644 index a0bada41967f1..0000000000000 --- a/polkadot/xcm/pallet-xnft/README.md +++ /dev/null @@ -1 +0,0 @@ -# xnft diff --git a/polkadot/xcm/pallet-xnft/src/lib.rs b/polkadot/xcm/pallet-xnft/src/lib.rs deleted file mode 100644 index ead4bab95f23f..0000000000000 --- a/polkadot/xcm/pallet-xnft/src/lib.rs +++ /dev/null @@ -1,287 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#![recursion_limit = "256"] -// Ensure we're `no_std` when compiling for Wasm. -#![cfg_attr(not(feature = "std"), no_std)] - -use frame_support::{ - pallet_prelude::*, - traits::{ - tokens::asset_ops::{ - common_asset_kinds::Instance, - common_strategies::{DeriveAndReportId, Owned, PredefinedId}, - AssetDefinition, Create, - }, - Incrementable, - }, -}; -use sp_runtime::DispatchResult; -use sp_std::prelude::*; -use xcm::latest::prelude::*; -use xcm_builder::unique_instances::{derivatives::*, NonFungibleAsset}; - -pub use pallet::*; - -/// The log target of this pallet. -pub const LOG_TARGET: &'static str = "runtime::xnft"; - -type DerivativeIdParamsOf = >::DerivativeIdParams; - -type DerivativeIdOf = >::DerivativeId; - -#[frame_support::pallet] -pub mod pallet { - use super::*; - - /// The in-code storage version. - const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); - - #[pallet::pallet] - #[pallet::storage_version(STORAGE_VERSION)] - pub struct Pallet(PhantomData<(T, I)>); - - #[pallet::config] - /// The module configuration trait. - pub trait Config: frame_system::Config { - /// The overarching event type. - type RuntimeEvent: From> - + IsType<::RuntimeEvent>; - - type DerivativeIdParams: Member + Parameter + MaxEncodedLen; - type DerivativeId: Member + Parameter + MaxEncodedLen; - } - - #[pallet::storage] - #[pallet::getter(fn foreign_asset_to_derivative_id_params)] - pub type ForeignAssetToDerivativeIdParams, I: 'static = ()> = - StorageMap<_, Blake2_128, AssetId, DerivativeIdParamsOf, OptionQuery>; - - #[pallet::storage] - #[pallet::getter(fn derivative_id_params_to_foreign_asset)] - pub type DerivativeIdParamsToForeignAsset, I: 'static = ()> = - StorageMap<_, Blake2_128, DerivativeIdParamsOf, AssetId, OptionQuery>; - - #[pallet::storage] - #[pallet::getter(fn foreign_nft_to_derivative_id)] - pub type ForeignNftToDerivativeId, I: 'static = ()> = StorageDoubleMap< - _, - Blake2_128, - AssetId, - Blake2_128, - AssetInstance, - DerivativeIdOf, - OptionQuery, - >; - - #[pallet::storage] - #[pallet::getter(fn derivative_id_to_foreign_nft)] - pub type DerivativeIdToForeignNft, I: 'static = ()> = - StorageMap<_, Blake2_128, DerivativeIdOf, NonFungibleAsset, OptionQuery>; - - #[pallet::storage] - pub type NextComposableInstanceIdSuffix, I: 'static = ()> - where - T::DerivativeId: CompositeDerivativeId, - = StorageMap< - _, - Blake2_128, - T::DerivativeIdParams, - ::Suffix, - OptionQuery, - >; - - #[pallet::event] - #[pallet::generate_deposit(pub(crate) fn deposit_event)] - pub enum Event, I: 'static = ()> { - /// A derivative instance is registered - /// and bound to a certain foreign nonfungible asset instance. - DerivativeInstanceRegistered { - /// The ID of the derivative instance. - derivative_id: T::DerivativeId, - - /// The XCM ID of the bound foreign nonfungible asset instance. - foreign_nonfungible: NonFungibleAsset, - }, - - /// A derivative instance is de-registered. - DerivativeInstanceDeregistered { - /// The ID of the derivative instance. - derivative_id: T::DerivativeId, - - /// The XCM ID of the bound foreign nonfungible asset instance. - foreign_nonfungible: NonFungibleAsset, - }, - } - - #[pallet::error] - pub enum Error { - /// Can't perform an operation due to the invalid state of storage. - InvalidState, - - /// Unable to set the next instance ID suffix. - CantSetNextInstanceIdSuffix, - } - - #[pallet::call] - impl, I: 'static> Pallet {} -} - -pub struct DerivativeIdParamsRegistry(PhantomData); -impl, I: 'static> DerivativesRegistry - for DerivativeIdParamsRegistry> -{ - fn try_register_derivative( - foreign_asset_id: &AssetId, - derivative_id_params: &T::DerivativeIdParams, - ) -> DispatchResult { - >::insert(foreign_asset_id, derivative_id_params); - >::insert(derivative_id_params, foreign_asset_id); - - Ok(()) - } - - fn try_deregister_derivative(derivative_id_params: &T::DerivativeIdParams) -> DispatchResult { - let foreign_asset_id = - >::take(&derivative_id_params) - .ok_or(Error::::InvalidState)?; - - >::remove(&foreign_asset_id); - - Ok(()) - } - - fn get_derivative(original: &AssetId) -> Option { - >::get(original) - } - - fn get_original(derivative_id_params: &T::DerivativeIdParams) -> Option { - >::get(derivative_id_params) - } -} - -pub struct DerivativeInstancesRegistry(PhantomData); -impl, I: 'static> DerivativesRegistry - for DerivativeInstancesRegistry> -{ - fn try_register_derivative( - foreign_nonfungible @ (original_asset_id, original_asset_instance): &NonFungibleAsset, - derivative_id: &T::DerivativeId, - ) -> DispatchResult { - >::insert( - original_asset_id, - original_asset_instance, - derivative_id, - ); - >::insert(derivative_id, foreign_nonfungible); - - >::deposit_event(Event::::DerivativeInstanceRegistered { - derivative_id: derivative_id.clone(), - foreign_nonfungible: foreign_nonfungible.clone(), - }); - - Ok(()) - } - - fn try_deregister_derivative(derivative_id: &T::DerivativeId) -> DispatchResult { - let foreign_nonfungible = >::take(&derivative_id) - .ok_or(Error::::InvalidState)?; - - >::remove(&foreign_nonfungible.0, &foreign_nonfungible.1); - - >::deposit_event(Event::::DerivativeInstanceDeregistered { - derivative_id: derivative_id.clone(), - foreign_nonfungible, - }); - - Ok(()) - } - - fn get_derivative((asset_id, asset_instance): &NonFungibleAsset) -> Option { - >::get(asset_id, asset_instance) - } - - fn get_original(derivative: &T::DerivativeId) -> Option { - >::get(derivative) - } -} - -pub trait CompositeDerivativeId { - type Prefix: Member + Parameter + MaxEncodedLen; - type Suffix: Member + Parameter + MaxEncodedLen; - - fn compose(prefix: Self::Prefix, suffix: Self::Suffix) -> Self; -} - -impl CompositeDerivativeId for (Prefix, Suffix) -where - Prefix: Member + Parameter + MaxEncodedLen, - Suffix: Member + Parameter + MaxEncodedLen, -{ - type Prefix = Prefix; - type Suffix = Suffix; - - fn compose(prefix: Self::Prefix, suffix: Self::Suffix) -> Self { - (prefix, suffix) - } -} - -pub struct ConcatIncrementableIdOnCreate(PhantomData<(XnftPallet, CreateOp)>); -impl AssetDefinition - for ConcatIncrementableIdOnCreate -where - CreateOp: AssetDefinition, -{ - type Id = CreateOp::Id; -} -impl - Create>> - for ConcatIncrementableIdOnCreate, CreateOp> -where - T: Config, - I: 'static, - T::DerivativeId: CompositeDerivativeId, - ::Suffix: Incrementable, - CreateOp: Create>>, -{ - fn create( - strategy: Owned>, - ) -> Result { - let Owned { owner, id_assignment, .. } = strategy; - let derivative_id_params = id_assignment.params; - - let instance_id_suffix = >::get(&derivative_id_params) - .or(::Suffix::initial_value()) - .ok_or(>::CantSetNextInstanceIdSuffix)?; - - let next_instance_id_suffix = instance_id_suffix - .increment() - .ok_or(>::CantSetNextInstanceIdSuffix)?; - - let derivative_id = - T::DerivativeId::compose(derivative_id_params.clone(), instance_id_suffix); - - CreateOp::create(Owned::new(owner, PredefinedId::from(derivative_id.clone())))?; - - >::insert( - derivative_id_params, - next_instance_id_suffix, - ); - - Ok(derivative_id) - } -} diff --git a/umbrella/Cargo.toml b/umbrella/Cargo.toml index 900628b407cad..944e40bd30a32 100644 --- a/umbrella/Cargo.toml +++ b/umbrella/Cargo.toml @@ -162,7 +162,7 @@ std = [ "pallet-xcm-bridge-hub-router?/std", "pallet-xcm-bridge-hub?/std", "pallet-xcm?/std", - "pallet-xnft?/std", + "pallet-derivatives?/std", "parachains-common?/std", "parachains-runtimes-test-utils?/std", "polkadot-core-primitives?/std", @@ -341,7 +341,7 @@ runtime-benchmarks = [ "pallet-xcm-bridge-hub-router?/runtime-benchmarks", "pallet-xcm-bridge-hub?/runtime-benchmarks", "pallet-xcm?/runtime-benchmarks", - "pallet-xnft?/runtime-benchmarks", + "pallet-derivatives?/runtime-benchmarks", "parachains-common?/runtime-benchmarks", "polkadot-cli?/runtime-benchmarks", "polkadot-node-metrics?/runtime-benchmarks", @@ -473,7 +473,7 @@ try-runtime = [ "pallet-xcm-bridge-hub-router?/try-runtime", "pallet-xcm-bridge-hub?/try-runtime", "pallet-xcm?/try-runtime", - "pallet-xnft?/try-runtime", + "pallet-derivatives?/try-runtime", "polkadot-cli?/try-runtime", "polkadot-runtime-common?/try-runtime", "polkadot-runtime-parachains?/try-runtime", @@ -700,7 +700,7 @@ runtime = [ "pallet-xcm-benchmarks", "pallet-xcm-bridge-hub", "pallet-xcm-bridge-hub-router", - "pallet-xnft", + "pallet-derivatives", "parachains-common", "polkadot-core-primitives", "polkadot-parachain-primitives", @@ -1577,8 +1577,8 @@ path = "../bridges/modules/xcm-bridge-hub-router" default-features = false optional = true -[dependencies.pallet-xnft] -path = "../polkadot/xcm/pallet-xnft" +[dependencies.pallet-derivatives] +path = "../polkadot/xcm/pallet-derivatives" default-features = false optional = true diff --git a/umbrella/src/lib.rs b/umbrella/src/lib.rs index 0ed7ce8b6acfa..6aa19cdcd957e 100644 --- a/umbrella/src/lib.rs +++ b/umbrella/src/lib.rs @@ -753,9 +753,9 @@ pub use pallet_xcm_bridge_hub; #[cfg(feature = "pallet-xcm-bridge-hub-router")] pub use pallet_xcm_bridge_hub_router; -/// FRAME XNFT pallet. -#[cfg(feature = "pallet-xnft")] -pub use pallet_xnft; +/// XCM derivatives pallet. +#[cfg(feature = "pallet-derivatives")] +pub use pallet_derivatives; /// Logic which is common to all parachain runtimes. #[cfg(feature = "parachains-common")] From c1ff41efc8b809454458e482528a4963398dabdb Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Thu, 19 Sep 2024 15:57:34 +0200 Subject: [PATCH 28/41] refactor: simplify unique instances derivatives types --- .../src/unique_instances/adapter.rs | 49 +++++------ .../src/unique_instances/derivatives.rs | 87 +++---------------- 2 files changed, 34 insertions(+), 102 deletions(-) diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/adapter.rs b/polkadot/xcm/xcm-builder/src/unique_instances/adapter.rs index 5456e683e2a40..63425a8f8e373 100644 --- a/polkadot/xcm/xcm-builder/src/unique_instances/adapter.rs +++ b/polkadot/xcm/xcm-builder/src/unique_instances/adapter.rs @@ -7,6 +7,8 @@ use frame_support::traits::tokens::asset_ops::{ use xcm::latest::prelude::*; use xcm_executor::traits::{ConvertLocation, Error as MatchError, MatchesInstance, TransactAsset}; +use super::NonFungibleAsset; + const LOG_TARGET: &str = "xcm::unique_instances"; /// The `UniqueInstancesAdapter` implements the [`TransactAsset`] for unique instances (NFT-like @@ -110,33 +112,22 @@ where } /// The `UniqueInstancesDepositAdapter` implements the [`TransactAsset`] to create unique instances -/// (NFT-like entities), for which the `Matcher` can **not** deduce the instance ID from the XCM -/// [`AssetId`]. Instead, this adapter requires the `Matcher` to return -/// the derive ID parameters (the `DeriveIdParams`) for the [`DeriveAndReportId`] ID assignment -/// approach. -/// -/// The new instance will be created using the `InstanceCreateOp` and then deposited to a -/// beneficiary. -pub struct UniqueInstancesDepositAdapter< - AccountId, - AccountIdConverter, - DeriveIdParams, - Matcher, - InstanceCreateOp, ->(PhantomData<(AccountId, AccountIdConverter, DeriveIdParams, Matcher, InstanceCreateOp)>); - -impl TransactAsset - for UniqueInstancesDepositAdapter< - AccountId, - AccountIdConverter, - DeriveIdParams, - Matcher, - InstanceCreateOp, - > where +/// (NFT-like entities), for which no `Matcher` deduce the instance ID from the XCM +/// [`AssetId`]. Instead, this adapter requires the `InstanceCreateOp` to create an instance using +/// [`NonFungibleAsset`] as derive id parameters. +pub struct UniqueInstancesDepositAdapter( + PhantomData<(AccountId, AccountIdConverter, InstanceCreateOp)>, +); + +impl TransactAsset + for UniqueInstancesDepositAdapter +where AccountIdConverter: ConvertLocation, - Matcher: MatchesInstance, InstanceCreateOp: AssetDefinition - + Create>>, + + Create< + Instance, + Owned>, + >, { fn deposit_asset(what: &Asset, who: &Location, context: Option<&XcmContext>) -> XcmResult { log::trace!( @@ -147,11 +138,15 @@ impl T context, ); - let derive_id_params = Matcher::matches_instance(what)?; + let asset = match what.fun { + Fungibility::NonFungible(asset_instance) => (what.id.clone(), asset_instance), + _ => return Err(MatchError::AssetNotHandled.into()), + }; + let who = AccountIdConverter::convert_location(who) .ok_or(MatchError::AccountIdConversionFailed)?; - InstanceCreateOp::create(Owned::new(who, DeriveAndReportId::from(derive_id_params))) + InstanceCreateOp::create(Owned::new(who, DeriveAndReportId::from(asset))) .map(|_reported_id| ()) .map_err(|e| XcmError::FailedToTransactAsset(e.into())) } diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs b/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs index ff0ac523cd149..2c6c081bd7030 100644 --- a/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs +++ b/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs @@ -31,29 +31,17 @@ pub trait DerivativesRegistry { fn get_original(derivative: &Derivative) -> Option; } -/// Parameters for registering a new derivative instance. -pub struct DerivativeRegisterParams { - /// The XCM identified of the original unique instance. - pub foreign_nonfungible: NonFungibleAsset, - - /// The derive ID parameters for the [`DeriveAndReportId`] - /// to create a new derivative instance. - pub derivative_id_params: DerivativeIdParams, -} - /// The `RegisterOnCreate` is a utility for creating a new instance /// and immediately binding it to the original instance using the [`DerivativesRegistry`]. /// -/// It implements the [`Create`] operation using the [`Owned`] strategy -/// and the [`DeriveAndReportId`] ID assignment, accepting the [`DerivativeRegisterParams`] as the -/// parameters. +/// It implements the [`Create`] operation using the [`Owned`] strategy. /// /// The `RegisterOnCreate` will create a new derivative instance using the `InstanceOps` /// and then bind it to the original instance via the `Registry`'s /// [`try_register_derivative`](DerivativesRegistry::try_register_derivative). /// /// The `InstanceOps` must be capable of creating a new instance by deriving the ID -/// based on the [`derivative_id_params`](DerivativeRegisterParams::derivative_id_params). +/// based on the [`NonFungibleAsset`]. pub struct RegisterOnCreate(PhantomData<(Registry, InstanceOps)>); impl AssetDefinition for RegisterOnCreate where @@ -61,40 +49,25 @@ where { type Id = InstanceOps::Id; } -impl - Create< - Instance, - Owned< - AccountId, - DeriveAndReportId, InstanceOps::Id>, - >, - > for RegisterOnCreate +impl + Create>> + for RegisterOnCreate where Registry: DerivativesRegistry, InstanceOps: AssetDefinition - + Create>>, + + Create>>, { fn create( - strategy: Owned< - AccountId, - DeriveAndReportId, InstanceOps::Id>, - >, + strategy: Owned>, ) -> Result { let Owned { owner, id_assignment, .. } = strategy; - let DerivativeRegisterParams { foreign_nonfungible, derivative_id_params } = - id_assignment.params; - - if Registry::get_derivative(&foreign_nonfungible).is_some() { - return Err(DispatchError::Other( - "an attempt to register a duplicate of an existing derivative instance", - )); - } + let asset = id_assignment.params; let instance_id = - InstanceOps::create(Owned::new(owner, DeriveAndReportId::from(derivative_id_params)))?; + InstanceOps::create(Owned::new(owner, DeriveAndReportId::from(asset.clone())))?; - Registry::try_register_derivative(&foreign_nonfungible, &instance_id)?; + Registry::try_register_derivative(&asset, &instance_id)?; Ok(instance_id) } @@ -122,44 +95,8 @@ where InstanceOps: Destroy>, { fn destroy(id: &Self::Id, strategy: IfOwnedBy) -> DispatchResult { - if Registry::get_original(id).is_none() { - return Err(DispatchError::Other( - "an attempt to deregister an instance that isn't a derivative", - )); - } - - InstanceOps::destroy(id, strategy)?; - - Registry::try_deregister_derivative(id) - } -} - -/// The `MatchDerivativeRegisterParams` is an XCM Matcher -/// that returns [the parameters](DerivativeRegisterParams) for registering a new derivative -/// instance. -/// -/// This Matcher can be used in the -/// [`UniqueInstancesDepositAdapter`](super::UniqueInstancesDepositAdapter). -pub struct MatchDerivativeRegisterParams(PhantomData); -impl, DerivativeIdParams> - MatchesInstance> - for MatchDerivativeRegisterParams -{ - fn matches_instance( - asset: &Asset, - ) -> Result, Error> { - match asset.fun { - Fungibility::NonFungible(asset_instance) => { - let derivative_id_params = - Registry::get_derivative(&asset.id).ok_or(Error::AssetNotHandled)?; - - Ok(DerivativeRegisterParams { - foreign_nonfungible: (asset.id.clone(), asset_instance), - derivative_id_params, - }) - }, - Fungibility::Fungible(_) => Err(Error::AssetNotHandled), - } + Registry::try_deregister_derivative(id)?; + InstanceOps::destroy(id, strategy) } } From 7bfb0fddda4ed441477feacf88607ee2e9fb50ea Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Thu, 19 Sep 2024 17:55:40 +0200 Subject: [PATCH 29/41] refactor: remove RegisterOnCreate/DeregisterOnDestroy --- .../src/unique_instances/derivatives.rs | 69 ------------------- 1 file changed, 69 deletions(-) diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs b/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs index 2c6c081bd7030..2bac41d92adbc 100644 --- a/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs +++ b/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs @@ -31,75 +31,6 @@ pub trait DerivativesRegistry { fn get_original(derivative: &Derivative) -> Option; } -/// The `RegisterOnCreate` is a utility for creating a new instance -/// and immediately binding it to the original instance using the [`DerivativesRegistry`]. -/// -/// It implements the [`Create`] operation using the [`Owned`] strategy. -/// -/// The `RegisterOnCreate` will create a new derivative instance using the `InstanceOps` -/// and then bind it to the original instance via the `Registry`'s -/// [`try_register_derivative`](DerivativesRegistry::try_register_derivative). -/// -/// The `InstanceOps` must be capable of creating a new instance by deriving the ID -/// based on the [`NonFungibleAsset`]. -pub struct RegisterOnCreate(PhantomData<(Registry, InstanceOps)>); -impl AssetDefinition for RegisterOnCreate -where - InstanceOps: AssetDefinition, -{ - type Id = InstanceOps::Id; -} -impl - Create>> - for RegisterOnCreate -where - Registry: DerivativesRegistry, - InstanceOps: AssetDefinition - + Create>>, -{ - fn create( - strategy: Owned>, - ) -> Result { - let Owned { owner, id_assignment, .. } = strategy; - - let asset = id_assignment.params; - - let instance_id = - InstanceOps::create(Owned::new(owner, DeriveAndReportId::from(asset.clone())))?; - - Registry::try_register_derivative(&asset, &instance_id)?; - - Ok(instance_id) - } -} - -/// The `DeregisterOnDestroy` is a utility for destroying a derivative instance -/// and immediately removing its binding to the original instance via the [`DerivativesRegistry`]. -/// -/// It implements the [`Destroy`] operation using the [`IfOwnedBy`] strategy. -/// -/// The `DeregisterOnDestroy` will destroy a derivative instance using the `InstanceOps` -/// and then unbind it from the original instance via the `Registry`'s -/// [`try_deregister_derivative`](DerivativesRegistry::try_deregister_derivative). -pub struct DeregisterOnDestroy(PhantomData<(Registry, InstanceOps)>); -impl AssetDefinition for DeregisterOnDestroy -where - InstanceOps: AssetDefinition, -{ - type Id = InstanceOps::Id; -} -impl Destroy> - for DeregisterOnDestroy -where - Registry: DerivativesRegistry, - InstanceOps: Destroy>, -{ - fn destroy(id: &Self::Id, strategy: IfOwnedBy) -> DispatchResult { - Registry::try_deregister_derivative(id)?; - InstanceOps::destroy(id, strategy) - } -} - /// The `MatchDerivativeInstances` is an XCM Matcher /// that uses a [`DerivativesRegistry`] to match the XCM identification of the original instance /// to a derivative instance. From 1b90a4809e6bd9bbc284f497978ec78eb729c571 Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Thu, 19 Sep 2024 18:23:51 +0200 Subject: [PATCH 30/41] refactor: use result type in all registry's methods --- polkadot/xcm/pallet-derivatives/src/lib.rs | 18 ++++++++++++------ .../src/unique_instances/derivatives.rs | 8 ++++---- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/polkadot/xcm/pallet-derivatives/src/lib.rs b/polkadot/xcm/pallet-derivatives/src/lib.rs index 9c46a7f7439d7..f4ed0e284c0ee 100644 --- a/polkadot/xcm/pallet-derivatives/src/lib.rs +++ b/polkadot/xcm/pallet-derivatives/src/lib.rs @@ -20,7 +20,7 @@ #![cfg_attr(not(feature = "std"), no_std)] use frame_support::pallet_prelude::*; -use sp_runtime::DispatchResult; +use sp_runtime::{DispatchError, DispatchResult}; use xcm_builder::unique_instances::derivatives::DerivativesRegistry; pub use pallet::*; @@ -79,8 +79,14 @@ pub mod pallet { /// A derivative already exists. DerivativeAlreadyExists, - /// Failed to deregister a non-registered derivative + /// Failed to deregister a non-registered derivative. NoDerivativeToDeregister, + + /// Failed to get a derivative for the given original. + DerivativeNotFound, + + /// Failed to get an original for the given derivative. + OriginalNotFound, } #[pallet::call] @@ -124,11 +130,11 @@ impl, I: 'static> DerivativesRegistry, DerivativeO Ok(()) } - fn get_derivative(original: &OriginalOf) -> Option> { - >::get(original) + fn get_derivative(original: &OriginalOf) -> Result, DispatchError> { + >::get(original).ok_or(Error::::DerivativeNotFound.into()) } - fn get_original(derivative: &DerivativeOf) -> Option> { - >::get(derivative) + fn get_original(derivative: &DerivativeOf) -> Result, DispatchError> { + >::get(derivative).ok_or(Error::::OriginalNotFound.into()) } } diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs b/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs index 2bac41d92adbc..bf81d98ed36aa 100644 --- a/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs +++ b/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs @@ -26,9 +26,9 @@ pub trait DerivativesRegistry { fn try_deregister_derivative(derivative: &Derivative) -> DispatchResult; - fn get_derivative(original: &Original) -> Option; + fn get_derivative(original: &Original) -> Result; - fn get_original(derivative: &Derivative) -> Option; + fn get_original(derivative: &Derivative) -> Result; } /// The `MatchDerivativeInstances` is an XCM Matcher @@ -42,7 +42,7 @@ impl, DerivativeId match asset.fun { Fungibility::NonFungible(asset_instance) => Registry::get_derivative(&(asset.id.clone(), asset_instance)) - .ok_or(Error::AssetNotHandled), + .map_err(|_| Error::AssetNotHandled), Fungibility::Fungible(_) => Err(Error::AssetNotHandled), } } @@ -77,7 +77,7 @@ impl< fn matches_instance(asset: &Asset) -> Result { let instance_id = Matcher::matches_instance(asset)?; - ensure!(Registry::get_original(&instance_id).is_none(), Error::AssetNotHandled,); + ensure!(Registry::get_original(&instance_id).is_err(), Error::AssetNotHandled); Ok(instance_id) } From 15c0c5363681b5cbcfeacfb4aae998d927d77adb Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Thu, 19 Sep 2024 18:50:34 +0200 Subject: [PATCH 31/41] feat: add AssetIdOf --- substrate/frame/support/src/traits/tokens/asset_ops.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/substrate/frame/support/src/traits/tokens/asset_ops.rs b/substrate/frame/support/src/traits/tokens/asset_ops.rs index 9a03a3fdfdeda..5c4a03d9d9320 100644 --- a/substrate/frame/support/src/traits/tokens/asset_ops.rs +++ b/substrate/frame/support/src/traits/tokens/asset_ops.rs @@ -42,6 +42,9 @@ pub trait AssetDefinition { type Id; } +/// Get the `Id` type of the asset kind. +pub type AssetIdOf = >::Id; + /// A strategy for use in the [`InspectMetadata`] implementations. /// /// The common inspect strategies are: From a7e18094152fa140fd167dcdc8913ea9fc83336c Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Fri, 20 Sep 2024 03:47:41 +0200 Subject: [PATCH 32/41] refactor: use options to get original/derivative --- polkadot/xcm/pallet-derivatives/src/lib.rs | 10 +++++----- .../xcm-builder/src/unique_instances/derivatives.rs | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/polkadot/xcm/pallet-derivatives/src/lib.rs b/polkadot/xcm/pallet-derivatives/src/lib.rs index f4ed0e284c0ee..873bd05f1629e 100644 --- a/polkadot/xcm/pallet-derivatives/src/lib.rs +++ b/polkadot/xcm/pallet-derivatives/src/lib.rs @@ -20,7 +20,7 @@ #![cfg_attr(not(feature = "std"), no_std)] use frame_support::pallet_prelude::*; -use sp_runtime::{DispatchError, DispatchResult}; +use sp_runtime::DispatchResult; use xcm_builder::unique_instances::derivatives::DerivativesRegistry; pub use pallet::*; @@ -130,11 +130,11 @@ impl, I: 'static> DerivativesRegistry, DerivativeO Ok(()) } - fn get_derivative(original: &OriginalOf) -> Result, DispatchError> { - >::get(original).ok_or(Error::::DerivativeNotFound.into()) + fn get_derivative(original: &OriginalOf) -> Option> { + >::get(original) } - fn get_original(derivative: &DerivativeOf) -> Result, DispatchError> { - >::get(derivative).ok_or(Error::::OriginalNotFound.into()) + fn get_original(derivative: &DerivativeOf) -> Option> { + >::get(derivative) } } diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs b/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs index bf81d98ed36aa..fc1e628f04dae 100644 --- a/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs +++ b/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs @@ -9,7 +9,7 @@ use frame_support::{ AssetDefinition, Create, Destroy, }, }; -use sp_runtime::{DispatchError, DispatchResult}; +use sp_runtime::DispatchResult; use xcm::latest::prelude::*; use xcm_executor::traits::{Error, MatchesInstance}; @@ -26,9 +26,9 @@ pub trait DerivativesRegistry { fn try_deregister_derivative(derivative: &Derivative) -> DispatchResult; - fn get_derivative(original: &Original) -> Result; + fn get_derivative(original: &Original) -> Option; - fn get_original(derivative: &Derivative) -> Result; + fn get_original(derivative: &Derivative) -> Option; } /// The `MatchDerivativeInstances` is an XCM Matcher @@ -42,7 +42,7 @@ impl, DerivativeId match asset.fun { Fungibility::NonFungible(asset_instance) => Registry::get_derivative(&(asset.id.clone(), asset_instance)) - .map_err(|_| Error::AssetNotHandled), + .ok_or(Error::AssetNotHandled), Fungibility::Fungible(_) => Err(Error::AssetNotHandled), } } @@ -77,7 +77,7 @@ impl< fn matches_instance(asset: &Asset) -> Result { let instance_id = Matcher::matches_instance(asset)?; - ensure!(Registry::get_original(&instance_id).is_err(), Error::AssetNotHandled); + ensure!(Registry::get_original(&instance_id).is_none(), Error::AssetNotHandled); Ok(instance_id) } From a71ecad1a02ad92ef8f25ecde2ec48924486c78a Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Fri, 11 Oct 2024 20:08:21 +0200 Subject: [PATCH 33/41] feat: add IterDerivativesRegistry --- polkadot/xcm/pallet-derivatives/src/lib.rs | 22 ++++++++++++++++--- .../src/unique_instances/derivatives.rs | 12 +++++++++- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/polkadot/xcm/pallet-derivatives/src/lib.rs b/polkadot/xcm/pallet-derivatives/src/lib.rs index 873bd05f1629e..03aa1ff121d68 100644 --- a/polkadot/xcm/pallet-derivatives/src/lib.rs +++ b/polkadot/xcm/pallet-derivatives/src/lib.rs @@ -21,7 +21,7 @@ use frame_support::pallet_prelude::*; use sp_runtime::DispatchResult; -use xcm_builder::unique_instances::derivatives::DerivativesRegistry; +use xcm_builder::unique_instances::derivatives::{DerivativesRegistry, IterDerivativesRegistry}; pub use pallet::*; @@ -57,12 +57,12 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn original_to_derivative)] pub type OriginalToDerivative, I: 'static = ()> = - StorageMap<_, Blake2_128, OriginalOf, DerivativeOf, OptionQuery>; + StorageMap<_, Blake2_128Concat, OriginalOf, DerivativeOf, OptionQuery>; #[pallet::storage] #[pallet::getter(fn derivative_to_original)] pub type DerivativeToOriginal, I: 'static = ()> = - StorageMap<_, Blake2_128, DerivativeOf, OriginalOf, OptionQuery>; + StorageMap<_, Blake2_128Concat, DerivativeOf, OriginalOf, OptionQuery>; #[pallet::event] #[pallet::generate_deposit(pub(crate) fn deposit_event)] @@ -138,3 +138,19 @@ impl, I: 'static> DerivativesRegistry, DerivativeO >::get(derivative) } } + +impl, I: 'static> IterDerivativesRegistry, DerivativeOf> + for Pallet +{ + fn iter_originals() -> impl Iterator> { + >::iter_keys() + } + + fn iter_derivatives() -> impl Iterator> { + >::iter_values() + } + + fn iter() -> impl Iterator, DerivativeOf)> { + >::iter() + } +} diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs b/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs index fc1e628f04dae..ed6c0df4f687b 100644 --- a/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs +++ b/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs @@ -18,8 +18,9 @@ use super::NonFungibleAsset; /// A registry abstracts the mapping between an `Original` entity and a `Derivative` entity. /// /// The primary use cases of the registry are: +/// * a map between an `AssetId` and an chain-local asset ID. +/// For instance, it could be chain-local currency ID or an NFT collection ID. /// * a map between a [`NonFungibleAsset`] and a derivative instance ID -/// * a map between an [`AssetId`] and a derive ID parameters for the [`DeriveAndReportId`] /// to create a new derivative instance pub trait DerivativesRegistry { fn try_register_derivative(original: &Original, derivative: &Derivative) -> DispatchResult; @@ -31,6 +32,15 @@ pub trait DerivativesRegistry { fn get_original(derivative: &Derivative) -> Option; } +/// Iterator utilities for a derivatives registry. +pub trait IterDerivativesRegistry { + fn iter_originals() -> impl Iterator; + + fn iter_derivatives() -> impl Iterator; + + fn iter() -> impl Iterator; +} + /// The `MatchDerivativeInstances` is an XCM Matcher /// that uses a [`DerivativesRegistry`] to match the XCM identification of the original instance /// to a derivative instance. From df878fd6faf3639d4583786f0c4fa66d99f2050b Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Sun, 13 Oct 2024 21:18:19 +0200 Subject: [PATCH 34/41] fix: remove unneeded imports --- .../xcm/xcm-builder/src/unique_instances/derivatives.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs b/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs index ed6c0df4f687b..98b64e9f4bcc3 100644 --- a/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs +++ b/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs @@ -1,14 +1,7 @@ //! Utilities for working with unique instances derivatives. use core::marker::PhantomData; -use frame_support::{ - ensure, - traits::tokens::asset_ops::{ - common_asset_kinds::Instance, - common_strategies::{DeriveAndReportId, IfOwnedBy, Owned}, - AssetDefinition, Create, Destroy, - }, -}; +use frame_support::ensure; use sp_runtime::DispatchResult; use xcm::latest::prelude::*; use xcm_executor::traits::{Error, MatchesInstance}; From c45b442e54c99be13c44899cba53ba9448927d56 Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Thu, 24 Oct 2024 06:56:10 +0200 Subject: [PATCH 35/41] refactor: remove AssetKind notion --- .../support/src/traits/tokens/asset_ops.rs | 68 ++++++------------- .../class.rs => asset_ops/collection.rs} | 48 ++++++------- .../instance.rs => asset_ops/item.rs} | 64 ++++++++--------- substrate/frame/uniques/src/asset_ops/mod.rs | 5 ++ .../frame/uniques/src/impl_asset_ops/mod.rs | 2 - substrate/frame/uniques/src/lib.rs | 2 +- 6 files changed, 83 insertions(+), 106 deletions(-) rename substrate/frame/uniques/src/{impl_asset_ops/class.rs => asset_ops/collection.rs} (70%) rename substrate/frame/uniques/src/{impl_asset_ops/instance.rs => asset_ops/item.rs} (65%) create mode 100644 substrate/frame/uniques/src/asset_ops/mod.rs delete mode 100644 substrate/frame/uniques/src/impl_asset_ops/mod.rs diff --git a/substrate/frame/support/src/traits/tokens/asset_ops.rs b/substrate/frame/support/src/traits/tokens/asset_ops.rs index 5c4a03d9d9320..dbf767e169c9b 100644 --- a/substrate/frame/support/src/traits/tokens/asset_ops.rs +++ b/substrate/frame/support/src/traits/tokens/asset_ops.rs @@ -12,10 +12,7 @@ //! Also, all the operations above (except the `Create` operation) use //! the [`AssetDefinition`] to retrieve the `Id` type of the asset. //! -//! Each asset operation can be implemented for different asset kinds -//! such as [`Class`](common_asset_kinds::Class) and [`Instance`](common_asset_kinds::Instance). -//! -//! Also, an asset operation can be implemented multiple times +//! An asset operation can be implemented multiple times //! using different strategies associated with this operation. //! //! A strategy defines the operation behavior, @@ -26,24 +23,15 @@ use core::marker::PhantomData; use sp_runtime::DispatchError; use sp_std::vec::Vec; -/// Trait for defining an asset of a certain kind. +/// Trait for defining an asset. /// The definition must provide the `Id` type to identify the asset. -/// -/// The common asset kinds are: -/// * The [`Class`](common_asset_kinds::Class) asset kind is of assets that resemble class-like -/// entities. For example, a collection of non-fungible tokens belongs to this kind. -/// * The [`Instance`](common_asset_kinds::Instance) asset kind is of assets that resemble concrete -/// instances of something. For example, a non-fungible token (which may or may not be part of a -/// certain class) belongs to this kind. -/// -/// Other asset kinds can be defined. -pub trait AssetDefinition { +pub trait AssetDefinition { /// Type for identifying the asset. type Id; } -/// Get the `Id` type of the asset kind. -pub type AssetIdOf = >::Id; +/// Get the `Id` type of the asset definition. +pub type AssetIdOf = ::Id; /// A strategy for use in the [`InspectMetadata`] implementations. /// @@ -59,7 +47,7 @@ pub trait MetadataInspectStrategy { type Value; } -/// A trait representing the ability of a certain asset kind to **provide** its metadata +/// A trait representing the ability of a certain asset to **provide** its metadata /// information. /// /// This trait can be implemented multiple times using different @@ -67,9 +55,7 @@ pub trait MetadataInspectStrategy { /// /// An inspect strategy defines how the asset metadata is identified/retrieved /// and what [`Value`](MetadataInspectStrategy::Value) type is returned. -pub trait InspectMetadata: - AssetDefinition -{ +pub trait InspectMetadata: AssetDefinition { /// Inspect metadata information of the asset /// using the given `id` and the inspect `strategy`. /// @@ -97,16 +83,14 @@ pub trait MetadataUpdateStrategy { type Success; } -/// A trait representing the ability of a certain asset kind to **update** its metadata information. +/// A trait representing the ability of a certain asset to **update** its metadata information. /// /// This trait can be implemented multiple times using different /// [`update strategies`](MetadataUpdateStrategy). /// /// An update strategy defines how the asset metadata is identified /// and what [`Update`](MetadataUpdateStrategy::Update) type is used. -pub trait UpdateMetadata: - AssetDefinition -{ +pub trait UpdateMetadata: AssetDefinition { /// Update metadata information of the asset /// using the given `id`, the update `strategy`, and the `update` value. /// @@ -147,13 +131,13 @@ pub trait IdAssignment { type ReportedId; } -/// A trait representing the ability of a certain asset kind to be created. +/// A trait representing the ability of a certain asset to be created. /// /// This trait can be implemented multiple times using different /// [`"create" strategies`](CreateStrategy). /// /// A create strategy defines all aspects of asset creation including how an asset ID is assigned. -pub trait Create { +pub trait Create { /// Create a new asset using the provided `strategy`. fn create(strategy: Strategy) -> Result; } @@ -169,13 +153,13 @@ pub trait TransferStrategy { type Success; } -/// A trait representing the ability of a certain asset kind to be transferred. +/// A trait representing the ability of a certain asset to be transferred. /// /// This trait can be implemented multiple times using different /// [`transfer strategies`](TransferStrategy). /// /// A transfer strategy defines transfer parameters. -pub trait Transfer: AssetDefinition { +pub trait Transfer: AssetDefinition { /// Transfer the asset identified by the given `id` using the provided `strategy`. /// /// The ID type is retrieved from the [`AssetDefinition`]. @@ -195,13 +179,13 @@ pub trait DestroyStrategy { type Success; } -/// A trait representing the ability of a certain asset kind to be destroyed. +/// A trait representing the ability of a certain asset to be destroyed. /// /// This trait can be implemented multiple times using different /// [`destroy strategies`](DestroyStrategy). /// /// A destroy strategy defines destroy parameters and the result value type. -pub trait Destroy: AssetDefinition { +pub trait Destroy: AssetDefinition { /// Destroy the asset identified by the given `id` using the provided `strategy`. /// /// The ID type is retrieved from the [`AssetDefinition`]. @@ -219,13 +203,13 @@ pub trait StashStrategy { type Success; } -/// A trait representing the ability of a certain asset kind to be stashed. +/// A trait representing the ability of a certain asset to be stashed. /// /// This trait can be implemented multiple times using different /// [`stash strategies`](StashStrategy). /// /// A stash strategy defines stash parameters. -pub trait Stash: AssetDefinition { +pub trait Stash: AssetDefinition { /// Stash the asset identified by the given `id` using the provided `strategy`. /// /// The ID type is retrieved from the [`AssetDefinition`]. @@ -242,33 +226,19 @@ pub trait RestoreStrategy { type Success; } -/// A trait representing the ability of a certain asset kind to be restored. +/// A trait representing the ability of a certain asset to be restored. /// /// This trait can be implemented multiple times using different /// [`restore strategies`](RestoreStrategy). /// /// A restore strategy defines restore parameters. -pub trait Restore: AssetDefinition { +pub trait Restore: AssetDefinition { /// Restore the asset identified by the given `id` using the provided `strategy`. /// /// The ID type is retrieved from the [`AssetDefinition`]. fn restore(id: &Self::Id, strategy: Strategy) -> Result; } -/// This modules contains the common asset kinds. -pub mod common_asset_kinds { - /// The `Class` asset kind is of assets that resemble class-like entities. - /// For instance, a collection of non-fungible tokens is an asset of this kind. - pub struct Class; - - /// The `Instance` asset kind represents assets resembling instances of something. - /// For instance, a single non-fungible token is an asset of this kind. - /// - /// An instance asset is not necessarily bound to a class. - /// There could be "classless" instances. - pub struct Instance; -} - /// This modules contains the common asset ops strategies. pub mod common_strategies { use super::*; diff --git a/substrate/frame/uniques/src/impl_asset_ops/class.rs b/substrate/frame/uniques/src/asset_ops/collection.rs similarity index 70% rename from substrate/frame/uniques/src/impl_asset_ops/class.rs rename to substrate/frame/uniques/src/asset_ops/collection.rs index 961ccdb01d414..570d69caf9d49 100644 --- a/substrate/frame/uniques/src/impl_asset_ops/class.rs +++ b/substrate/frame/uniques/src/asset_ops/collection.rs @@ -1,9 +1,10 @@ -use crate::{asset_strategies::Attribute, *}; +use core::marker::PhantomData; + +use crate::{asset_strategies::Attribute, *, Collection as CollectionStorage}; use frame_support::{ ensure, traits::{ tokens::asset_ops::{ - common_asset_kinds::Class, common_strategies::{ Adminable, Bytes, IfOwnedByWithWitness, Ownership, PredefinedId, WithOrigin, WithWitness, @@ -17,22 +18,24 @@ use frame_support::{ use frame_system::ensure_signed; use sp_runtime::DispatchError; -impl, I: 'static> AssetDefinition for Pallet { +pub struct Collection(PhantomData); + +impl, I: 'static> AssetDefinition for Collection> { type Id = T::CollectionId; } -impl, I: 'static> InspectMetadata> for Pallet { +impl, I: 'static> InspectMetadata> for Collection> { fn inspect_metadata( collection: &Self::Id, _ownership: Ownership, ) -> Result { - Collection::::get(collection) + CollectionStorage::::get(collection) .map(|a| a.owner) .ok_or(Error::::UnknownCollection.into()) } } -impl, I: 'static> InspectMetadata for Pallet { +impl, I: 'static> InspectMetadata for Collection> { fn inspect_metadata(collection: &Self::Id, _bytes: Bytes) -> Result, DispatchError> { CollectionMetadataOf::::get(collection) .map(|m| m.data.into()) @@ -40,7 +43,7 @@ impl, I: 'static> InspectMetadata for Pallet { } } -impl<'a, T: Config, I: 'static> InspectMetadata>> for Pallet { +impl<'a, T: Config, I: 'static> InspectMetadata>> for Collection> { fn inspect_metadata( collection: &Self::Id, strategy: Bytes, @@ -55,8 +58,8 @@ impl<'a, T: Config, I: 'static> InspectMetadata>> } } -impl, I: 'static> Create>> - for Pallet +impl, I: 'static> Create>> + for Collection> { fn create( strategy: Adminable>, @@ -64,7 +67,7 @@ impl, I: 'static> Create>::do_create_collection( collection.clone(), owner.clone(), admin.clone(), @@ -79,9 +82,8 @@ impl, I: 'static> Create, I: 'static> Create< - Class, WithOrigin>>, - > for Pallet + > for Collection> { fn create( strategy: WithOrigin< @@ -105,23 +107,23 @@ impl, I: 'static> ensure!(signer == *owner, Error::::NoPermission); } - >::create(creation) + Self::create(creation) } } -impl, I: 'static> Destroy> for Pallet { +impl, I: 'static> Destroy> for Collection> { fn destroy( collection: &Self::Id, strategy: WithWitness, ) -> Result { let WithWitness(witness) = strategy; - Self::do_destroy_collection(collection.clone(), witness, None) + >::do_destroy_collection(collection.clone(), witness, None) } } impl, I: 'static> - Destroy>> for Pallet + Destroy>> for Collection> { fn destroy( collection: &Self::Id, @@ -131,12 +133,12 @@ impl, I: 'static> T::ForceOrigin::ensure_origin(origin)?; - >::destroy(collection, destroy) + Self::destroy(collection, destroy) } } -impl, I: 'static> Destroy> - for Pallet +impl, I: 'static> Destroy> + for Collection> { fn destroy( collection: &Self::Id, @@ -144,13 +146,13 @@ impl, I: 'static> Destroy Result { let IfOwnedByWithWitness { owner, witness } = strategy; - Self::do_destroy_collection(collection.clone(), witness, Some(owner)) + >::do_destroy_collection(collection.clone(), witness, Some(owner)) } } impl, I: 'static> - Destroy>> - for Pallet + Destroy>> + for Collection> { fn destroy( collection: &Self::Id, @@ -166,6 +168,6 @@ impl, I: 'static> ensure!(signer == owner, Error::::NoPermission); } - Self::do_destroy_collection(collection.clone(), witness, Some(owner)) + >::do_destroy_collection(collection.clone(), witness, Some(owner)) } } diff --git a/substrate/frame/uniques/src/impl_asset_ops/instance.rs b/substrate/frame/uniques/src/asset_ops/item.rs similarity index 65% rename from substrate/frame/uniques/src/impl_asset_ops/instance.rs rename to substrate/frame/uniques/src/asset_ops/item.rs index de7aefb47ea42..a3220e893d39e 100644 --- a/substrate/frame/uniques/src/impl_asset_ops/instance.rs +++ b/substrate/frame/uniques/src/asset_ops/item.rs @@ -1,9 +1,10 @@ -use crate::{asset_strategies::Attribute, *}; +use core::marker::PhantomData; + +use crate::{asset_strategies::Attribute, *, Item as ItemStorage}; use frame_support::{ dispatch::DispatchResult, ensure, traits::tokens::asset_ops::{ - common_asset_kinds::Instance, common_strategies::{ Bytes, CanTransfer, FromTo, IfOwnedBy, JustDo, Owned, Ownership, PredefinedId, WithOrigin, @@ -15,22 +16,24 @@ use frame_support::{ use frame_system::ensure_signed; use sp_runtime::DispatchError; -impl, I: 'static> AssetDefinition for Pallet { +pub struct Item(PhantomData); + +impl, I: 'static> AssetDefinition for Item> { type Id = (T::CollectionId, T::ItemId); } -impl, I: 'static> InspectMetadata> for Pallet { +impl, I: 'static> InspectMetadata> for Item> { fn inspect_metadata( (collection, item): &Self::Id, _ownership: Ownership, ) -> Result { - Item::::get(collection, item) + ItemStorage::::get(collection, item) .map(|a| a.owner) .ok_or(Error::::UnknownItem.into()) } } -impl, I: 'static> InspectMetadata for Pallet { +impl, I: 'static> InspectMetadata for Item> { fn inspect_metadata( (collection, item): &Self::Id, _bytes: Bytes, @@ -41,8 +44,8 @@ impl, I: 'static> InspectMetadata for Pallet } } -impl<'a, T: Config, I: 'static> InspectMetadata>> - for Pallet +impl<'a, T: Config, I: 'static> InspectMetadata>> + for Item> { fn inspect_metadata( (collection, item): &Self::Id, @@ -58,12 +61,12 @@ impl<'a, T: Config, I: 'static> InspectMetadata } } -impl, I: 'static> InspectMetadata for Pallet { +impl, I: 'static> InspectMetadata for Item> { fn inspect_metadata( (collection, item): &Self::Id, _can_transfer: CanTransfer, ) -> Result { - match (Collection::::get(collection), Item::::get(collection, item)) { + match (Collection::::get(collection), ItemStorage::::get(collection, item)) { (Some(cd), Some(id)) => Ok(!cd.is_frozen && !id.is_frozen), _ => Err(Error::::UnknownItem.into()), } @@ -71,7 +74,7 @@ impl, I: 'static> InspectMetadata for Pallet } impl, I: 'static> - Create>> for Pallet + Create>> for Item> { fn create( strategy: Owned>, @@ -79,7 +82,7 @@ impl, I: 'static> let Owned { owner, id_assignment, .. } = strategy; let (collection, item) = id_assignment.params; - Self::do_mint(collection.clone(), item, owner, |_| Ok(()))?; + >::do_mint(collection.clone(), item, owner, |_| Ok(()))?; Ok((collection, item)) } @@ -87,12 +90,11 @@ impl, I: 'static> impl, I: 'static> Create< - Instance, WithOrigin< T::RuntimeOrigin, Owned>, >, - > for Pallet + > for Item> { fn create( strategy: WithOrigin< @@ -105,7 +107,7 @@ impl, I: 'static> let signer = ensure_signed(origin)?; - Self::do_mint(collection.clone(), item, owner, |collection_details| { + >::do_mint(collection.clone(), item, owner, |collection_details| { ensure!(collection_details.issuer == signer, Error::::NoPermission); Ok(()) })?; @@ -114,16 +116,16 @@ impl, I: 'static> } } -impl, I: 'static> Transfer> for Pallet { +impl, I: 'static> Transfer> for Item> { fn transfer((collection, item): &Self::Id, strategy: JustDo) -> DispatchResult { let JustDo(dest) = strategy; - Self::do_transfer(collection.clone(), *item, dest, |_, _| Ok(())) + >::do_transfer(collection.clone(), *item, dest, |_, _| Ok(())) } } impl, I: 'static> - Transfer>> for Pallet + Transfer>> for Item> { fn transfer( (collection, item): &Self::Id, @@ -133,7 +135,7 @@ impl, I: 'static> let signer = ensure_signed(origin)?; - Self::do_transfer(collection.clone(), *item, dest.clone(), |collection_details, details| { + >::do_transfer(collection.clone(), *item, dest.clone(), |collection_details, details| { if details.owner != signer && collection_details.admin != signer { let approved = details.approved.take().map_or(false, |i| i == signer); ensure!(approved, Error::::NoPermission); @@ -143,25 +145,25 @@ impl, I: 'static> } } -impl, I: 'static> Transfer> for Pallet { +impl, I: 'static> Transfer> for Item> { fn transfer((collection, item): &Self::Id, strategy: FromTo) -> DispatchResult { let FromTo(from, to) = strategy; - Self::do_transfer(collection.clone(), *item, to.clone(), |_, details| { + >::do_transfer(collection.clone(), *item, to.clone(), |_, details| { ensure!(details.owner == from, Error::::WrongOwner); Ok(()) }) } } -impl, I: 'static> Destroy for Pallet { +impl, I: 'static> Destroy for Item> { fn destroy((collection, item): &Self::Id, _strategy: JustDo) -> DispatchResult { - Self::do_burn(collection.clone(), *item, |_, _| Ok(())) + >::do_burn(collection.clone(), *item, |_, _| Ok(())) } } -impl, I: 'static> Destroy> - for Pallet +impl, I: 'static> Destroy> + for Item> { fn destroy( id @ (collection, item): &Self::Id, @@ -169,17 +171,17 @@ impl, I: 'static> Destroy DispatchResult { let WithOrigin(origin, _just_do) = strategy; let details = - Item::::get(collection, item).ok_or(Error::::UnknownCollection)?; + ItemStorage::::get(collection, item).ok_or(Error::::UnknownCollection)?; - >::destroy(id, WithOrigin(origin, IfOwnedBy(details.owner))) + Self::destroy(id, WithOrigin(origin, IfOwnedBy(details.owner))) } } -impl, I: 'static> Destroy> for Pallet { +impl, I: 'static> Destroy> for Item> { fn destroy((collection, item): &Self::Id, strategy: IfOwnedBy) -> DispatchResult { let IfOwnedBy(who) = strategy; - Self::do_burn(collection.clone(), *item, |_, d| { + >::do_burn(collection.clone(), *item, |_, d| { ensure!(d.owner == who, Error::::NoPermission); Ok(()) }) @@ -187,7 +189,7 @@ impl, I: 'static> Destroy> for Pa } impl, I: 'static> - Destroy>> for Pallet + Destroy>> for Item> { fn destroy( (collection, item): &Self::Id, @@ -197,7 +199,7 @@ impl, I: 'static> let signer = ensure_signed(origin)?; - Self::do_burn(collection.clone(), *item, |collection_details, details| { + >::do_burn(collection.clone(), *item, |collection_details, details| { let is_permitted = collection_details.admin == signer || details.owner == signer; ensure!(is_permitted, Error::::NoPermission); ensure!(who == details.owner, Error::::WrongOwner); diff --git a/substrate/frame/uniques/src/asset_ops/mod.rs b/substrate/frame/uniques/src/asset_ops/mod.rs new file mode 100644 index 0000000000000..e2105654505f1 --- /dev/null +++ b/substrate/frame/uniques/src/asset_ops/mod.rs @@ -0,0 +1,5 @@ +mod collection; +mod item; + +pub use collection::Collection; +pub use item::Item; diff --git a/substrate/frame/uniques/src/impl_asset_ops/mod.rs b/substrate/frame/uniques/src/impl_asset_ops/mod.rs deleted file mode 100644 index 26143c510d873..0000000000000 --- a/substrate/frame/uniques/src/impl_asset_ops/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -mod class; -mod instance; diff --git a/substrate/frame/uniques/src/lib.rs b/substrate/frame/uniques/src/lib.rs index b168bbb2b68b0..7de56f63676ce 100644 --- a/substrate/frame/uniques/src/lib.rs +++ b/substrate/frame/uniques/src/lib.rs @@ -36,10 +36,10 @@ pub mod mock; mod tests; mod functions; -mod impl_asset_ops; mod impl_nonfungibles; mod types; +pub mod asset_ops; pub mod migration; pub mod weights; From 44a236632465b829317e705ca20a598e0e613735 Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Thu, 24 Oct 2024 06:57:11 +0200 Subject: [PATCH 36/41] refactor: remove AssetKind notion from xcm adapters --- .../assets/asset-hub-rococo/src/xcm_config.rs | 2 +- .../asset-hub-westend/src/xcm_config.rs | 2 +- .../src/unique_instances/adapter.rs | 12 ++--- .../xcm-builder/src/unique_instances/ops.rs | 51 +++++++++---------- 4 files changed, 32 insertions(+), 35 deletions(-) diff --git a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs index f8e9f8f5ecf05..7689bc9214a5b 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-rococo/src/xcm_config.rs @@ -153,7 +153,7 @@ pub type UniquesTransactor = UniqueInstancesAdapter< AccountId, LocationToAccountId, MatchInClassInstances, - Uniques, + pallet_uniques::asset_ops::Item, >; /// `AssetId`/`Balance` converter for `ForeignAssets`. diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs index c4a6e3f772f12..96aaff0f121d5 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/xcm_config.rs @@ -146,7 +146,7 @@ pub type UniquesTransactor = UniqueInstancesAdapter< AccountId, LocationToAccountId, MatchInClassInstances, - Uniques, + pallet_uniques::asset_ops::Item, >; /// `AssetId`/`Balance` converter for `ForeignAssets`. diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/adapter.rs b/polkadot/xcm/xcm-builder/src/unique_instances/adapter.rs index 63425a8f8e373..f3d2775a8422e 100644 --- a/polkadot/xcm/xcm-builder/src/unique_instances/adapter.rs +++ b/polkadot/xcm/xcm-builder/src/unique_instances/adapter.rs @@ -1,6 +1,5 @@ use core::marker::PhantomData; use frame_support::traits::tokens::asset_ops::{ - common_asset_kinds::Instance, common_strategies::{DeriveAndReportId, FromTo, IfOwnedBy, Owned, PredefinedId}, AssetDefinition, Create, Destroy, Transfer, }; @@ -38,10 +37,10 @@ impl TransactAsset where AccountIdConverter: ConvertLocation, Matcher: MatchesInstance, - InstanceOps: AssetDefinition - + Create>> - + Transfer> - + Destroy>, + InstanceOps: AssetDefinition + + Create>> + + Transfer> + + Destroy>, { fn deposit_asset(what: &Asset, who: &Location, context: Option<&XcmContext>) -> XcmResult { log::trace!( @@ -123,9 +122,8 @@ impl TransactAsset for UniqueInstancesDepositAdapter where AccountIdConverter: ConvertLocation, - InstanceCreateOp: AssetDefinition + InstanceCreateOp: AssetDefinition + Create< - Instance, Owned>, >, { diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/ops.rs b/polkadot/xcm/xcm-builder/src/unique_instances/ops.rs index fe16daed04593..f47b0899a609a 100644 --- a/polkadot/xcm/xcm-builder/src/unique_instances/ops.rs +++ b/polkadot/xcm/xcm-builder/src/unique_instances/ops.rs @@ -3,7 +3,6 @@ use core::marker::PhantomData; use frame_support::traits::{ tokens::asset_ops::{ - common_asset_kinds::Instance, common_strategies::{FromTo, IfOwnedBy, IfRestorable, Owned, PredefinedId}, AssetDefinition, Create, CreateStrategy, Destroy, DestroyStrategy, Restore, Stash, Transfer, TransferStrategy, @@ -20,41 +19,41 @@ use sp_runtime::{DispatchError, DispatchResult}; pub struct UniqueInstancesOps( PhantomData<(CreateOp, TransferOp, DestroyOp)>, ); -impl AssetDefinition +impl AssetDefinition for UniqueInstancesOps where - TransferOp: AssetDefinition, - DestroyOp: AssetDefinition, + TransferOp: AssetDefinition, + DestroyOp: AssetDefinition, { type Id = TransferOp::Id; } -impl Create +impl Create for UniqueInstancesOps where Strategy: CreateStrategy, - CreateOp: Create, + CreateOp: Create, { fn create(strategy: Strategy) -> Result { CreateOp::create(strategy) } } -impl Transfer +impl Transfer for UniqueInstancesOps where Strategy: TransferStrategy, - TransferOp: Transfer, - DestroyOp: AssetDefinition, + TransferOp: Transfer, + DestroyOp: AssetDefinition, { fn transfer(id: &Self::Id, strategy: Strategy) -> Result { TransferOp::transfer(id, strategy) } } -impl Destroy +impl Destroy for UniqueInstancesOps where Strategy: DestroyStrategy, - TransferOp: AssetDefinition, - DestroyOp: AssetDefinition + Destroy, + TransferOp: AssetDefinition, + DestroyOp: AssetDefinition + Destroy, { fn destroy(id: &Self::Id, strategy: Strategy) -> Result { DestroyOp::destroy(id, strategy) @@ -67,17 +66,17 @@ where /// using the [`FromTo`] strategy. Restoring with the [`IfRestorable`] is implemented symmetrically /// as the transfer from the stash account using the [`FromTo`] strategy. pub struct SimpleStash(PhantomData<(StashAccount, InstanceOps)>); -impl AssetDefinition for SimpleStash +impl AssetDefinition for SimpleStash where - InstanceOps: AssetDefinition, + InstanceOps: AssetDefinition, { type Id = InstanceOps::Id; } -impl Stash> +impl Stash> for SimpleStash where StashAccount: TypedGet, - InstanceOps: Transfer>, + InstanceOps: Transfer>, { fn stash( id: &Self::Id, @@ -86,11 +85,11 @@ where InstanceOps::transfer(id, FromTo(possible_owner, StashAccount::get())) } } -impl Restore> +impl Restore> for SimpleStash where StashAccount: TypedGet, - InstanceOps: Transfer>, + InstanceOps: Transfer>, { fn restore( id: &Self::Id, @@ -108,16 +107,16 @@ where /// The implemented [`Create`] operation can be used in the /// [`UniqueInstancesAdapter`](super::UniqueInstancesAdapter) via the [`UniqueInstancesOps`]. pub struct RestoreOnCreate(PhantomData); -impl AssetDefinition for RestoreOnCreate +impl AssetDefinition for RestoreOnCreate where - InstanceOps: AssetDefinition, + InstanceOps: AssetDefinition, { type Id = InstanceOps::Id; } -impl Create>> +impl Create>> for RestoreOnCreate where - InstanceOps: Restore>, + InstanceOps: Restore>, { fn create( strategy: Owned>, @@ -138,15 +137,15 @@ where /// The implemented [`Destroy`] operation can be used in the /// [`UniqueInstancesAdapter`](super::UniqueInstancesAdapter) via the [`UniqueInstancesOps`]. pub struct StashOnDestroy(PhantomData); -impl AssetDefinition for StashOnDestroy +impl AssetDefinition for StashOnDestroy where - InstanceOps: AssetDefinition, + InstanceOps: AssetDefinition, { type Id = InstanceOps::Id; } -impl Destroy> for StashOnDestroy +impl Destroy> for StashOnDestroy where - InstanceOps: Stash>, + InstanceOps: Stash>, { fn destroy(id: &Self::Id, strategy: IfOwnedBy) -> DispatchResult { InstanceOps::stash(id, strategy) From 1f050ea6be287a6caa8881c63284c75505db40e2 Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Thu, 24 Oct 2024 06:57:43 +0200 Subject: [PATCH 37/41] fix: fmt uniques asset-ops --- .../frame/uniques/src/asset_ops/collection.rs | 19 ++++++----- substrate/frame/uniques/src/asset_ops/item.rs | 33 ++++++++++--------- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/substrate/frame/uniques/src/asset_ops/collection.rs b/substrate/frame/uniques/src/asset_ops/collection.rs index 570d69caf9d49..e5bd2ca9765dc 100644 --- a/substrate/frame/uniques/src/asset_ops/collection.rs +++ b/substrate/frame/uniques/src/asset_ops/collection.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; -use crate::{asset_strategies::Attribute, *, Collection as CollectionStorage}; +use crate::{asset_strategies::Attribute, Collection as CollectionStorage, *}; use frame_support::{ ensure, traits::{ @@ -24,7 +24,9 @@ impl, I: 'static> AssetDefinition for Collection> { type Id = T::CollectionId; } -impl, I: 'static> InspectMetadata> for Collection> { +impl, I: 'static> InspectMetadata> + for Collection> +{ fn inspect_metadata( collection: &Self::Id, _ownership: Ownership, @@ -43,7 +45,9 @@ impl, I: 'static> InspectMetadata for Collection, I: 'static> InspectMetadata>> for Collection> { +impl<'a, T: Config, I: 'static> InspectMetadata>> + for Collection> +{ fn inspect_metadata( collection: &Self::Id, strategy: Bytes, @@ -81,9 +85,8 @@ impl, I: 'static> Create, I: 'static> - Create< - WithOrigin>>, - > for Collection> + Create>>> + for Collection> { fn create( strategy: WithOrigin< @@ -122,8 +125,8 @@ impl, I: 'static> Destroy> for Collecti } } -impl, I: 'static> - Destroy>> for Collection> +impl, I: 'static> Destroy>> + for Collection> { fn destroy( collection: &Self::Id, diff --git a/substrate/frame/uniques/src/asset_ops/item.rs b/substrate/frame/uniques/src/asset_ops/item.rs index a3220e893d39e..9384bd68c40bc 100644 --- a/substrate/frame/uniques/src/asset_ops/item.rs +++ b/substrate/frame/uniques/src/asset_ops/item.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; -use crate::{asset_strategies::Attribute, *, Item as ItemStorage}; +use crate::{asset_strategies::Attribute, Item as ItemStorage, *}; use frame_support::{ dispatch::DispatchResult, ensure, @@ -44,9 +44,7 @@ impl, I: 'static> InspectMetadata for Item> { } } -impl<'a, T: Config, I: 'static> InspectMetadata>> - for Item> -{ +impl<'a, T: Config, I: 'static> InspectMetadata>> for Item> { fn inspect_metadata( (collection, item): &Self::Id, strategy: Bytes, @@ -124,8 +122,8 @@ impl, I: 'static> Transfer> for Item, I: 'static> - Transfer>> for Item> +impl, I: 'static> Transfer>> + for Item> { fn transfer( (collection, item): &Self::Id, @@ -135,13 +133,18 @@ impl, I: 'static> let signer = ensure_signed(origin)?; - >::do_transfer(collection.clone(), *item, dest.clone(), |collection_details, details| { - if details.owner != signer && collection_details.admin != signer { - let approved = details.approved.take().map_or(false, |i| i == signer); - ensure!(approved, Error::::NoPermission); - } - Ok(()) - }) + >::do_transfer( + collection.clone(), + *item, + dest.clone(), + |collection_details, details| { + if details.owner != signer && collection_details.admin != signer { + let approved = details.approved.take().map_or(false, |i| i == signer); + ensure!(approved, Error::::NoPermission); + } + Ok(()) + }, + ) } } @@ -188,8 +191,8 @@ impl, I: 'static> Destroy> for Item, I: 'static> - Destroy>> for Item> +impl, I: 'static> Destroy>> + for Item> { fn destroy( (collection, item): &Self::Id, From 53c24d9575209d7a41fd81d8c044b4ef077afb37 Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Thu, 24 Oct 2024 06:58:22 +0200 Subject: [PATCH 38/41] fix: fmt unique-instances xcm adapter --- polkadot/xcm/xcm-builder/src/unique_instances/adapter.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/adapter.rs b/polkadot/xcm/xcm-builder/src/unique_instances/adapter.rs index f3d2775a8422e..1033d90397238 100644 --- a/polkadot/xcm/xcm-builder/src/unique_instances/adapter.rs +++ b/polkadot/xcm/xcm-builder/src/unique_instances/adapter.rs @@ -123,9 +123,7 @@ impl TransactAsset where AccountIdConverter: ConvertLocation, InstanceCreateOp: AssetDefinition - + Create< - Owned>, - >, + + Create>>, { fn deposit_asset(what: &Asset, who: &Location, context: Option<&XcmContext>) -> XcmResult { log::trace!( From 80385b916302eacaf3f871e52f0f3d1dbb7a76a4 Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Fri, 25 Oct 2024 16:26:57 +0200 Subject: [PATCH 39/41] refactor: derivatives traits and pallet --- .../pallet-derivatives/src/benchmarking.rs | 48 +++++ polkadot/xcm/pallet-derivatives/src/lib.rs | 191 +++++++++++++++++- .../src/unique_instances/derivatives.rs | 9 +- 3 files changed, 237 insertions(+), 11 deletions(-) create mode 100644 polkadot/xcm/pallet-derivatives/src/benchmarking.rs diff --git a/polkadot/xcm/pallet-derivatives/src/benchmarking.rs b/polkadot/xcm/pallet-derivatives/src/benchmarking.rs new file mode 100644 index 0000000000000..b272d44ce7b5d --- /dev/null +++ b/polkadot/xcm/pallet-derivatives/src/benchmarking.rs @@ -0,0 +1,48 @@ +use super::{Pallet as Derivatives, *}; +use frame_benchmarking::v2::*; + +pub struct Pallet, I: 'static = ()>(Derivatives); + +pub trait Config: super::Config { + fn original() -> OriginalOf; + + fn derivative_create_params() -> DerivativeCreateParamsOf; +} + +#[instance_benchmarks] +mod benchmarks { + use super::*; + + #[benchmark] + fn create_derivative() -> Result<(), BenchmarkError> { + let create_origin = >::try_successful_origin() + .map_err(|_| BenchmarkError::Weightless)?; + + let original = T::original(); + let params = T::derivative_create_params(); + + #[extrinsic_call] + _(create_origin as T::RuntimeOrigin, original, params); + + Ok(()) + } + + #[benchmark] + fn destroy_derivative() -> Result<(), BenchmarkError> { + let create_origin = >::try_successful_origin() + .map_err(|_| BenchmarkError::Weightless)?; + + let destroy_origin = >::try_successful_origin() + .map_err(|_| BenchmarkError::Weightless)?; + + let original = T::original(); + let params = T::derivative_create_params(); + + >::create_derivative(create_origin, original.clone(), params)?; + + #[extrinsic_call] + _(destroy_origin as T::RuntimeOrigin, original); + + Ok(()) + } +} diff --git a/polkadot/xcm/pallet-derivatives/src/lib.rs b/polkadot/xcm/pallet-derivatives/src/lib.rs index 03aa1ff121d68..6e78e5f65b7e2 100644 --- a/polkadot/xcm/pallet-derivatives/src/lib.rs +++ b/polkadot/xcm/pallet-derivatives/src/lib.rs @@ -19,18 +19,32 @@ // Ensure we're `no_std` when compiling for Wasm. #![cfg_attr(not(feature = "std"), no_std)] -use frame_support::pallet_prelude::*; +use frame_support::{ + pallet_prelude::*, + traits::tokens::asset_ops::{ + common_strategies::JustDo, AssetDefinition, Create, CreateStrategy, Destroy, + DestroyStrategy, + }, +}; +use frame_system::{pallet_prelude::*, EnsureNever}; +use scale_info::TypeInfo; use sp_runtime::DispatchResult; -use xcm_builder::unique_instances::derivatives::{DerivativesRegistry, IterDerivativesRegistry}; +use xcm_builder::unique_instances::{ + derivatives::{DerivativesRegistry, IterDerivativesRegistry}, + DerivativesExtra, +}; pub use pallet::*; +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmarking; + /// The log target of this pallet. pub const LOG_TARGET: &'static str = "runtime::xcm::derivatives"; type OriginalOf = >::Original; - type DerivativeOf = >::Derivative; +type DerivativeExtraOf = >::DerivativeExtra; #[frame_support::pallet] pub mod pallet { @@ -52,6 +66,10 @@ pub mod pallet { type Original: Member + Parameter + MaxEncodedLen; type Derivative: Member + Parameter + MaxEncodedLen; + + type DerivativeExtra: Member + Parameter + MaxEncodedLen; + + type ExtrinsicsConfig: ExtrinsicsConfig; } #[pallet::storage] @@ -64,6 +82,11 @@ pub mod pallet { pub type DerivativeToOriginal, I: 'static = ()> = StorageMap<_, Blake2_128Concat, DerivativeOf, OriginalOf, OptionQuery>; + #[pallet::storage] + #[pallet::getter(fn derivative_extra)] + pub type DerivativeExtra, I: 'static = ()> = + StorageMap<_, Blake2_128Concat, DerivativeOf, DerivativeExtraOf, OptionQuery>; + #[pallet::event] #[pallet::generate_deposit(pub(crate) fn deposit_event)] pub enum Event, I: 'static = ()> { @@ -82,15 +105,46 @@ pub mod pallet { /// Failed to deregister a non-registered derivative. NoDerivativeToDeregister, - /// Failed to get a derivative for the given original. + /// Failed to find a derivative. DerivativeNotFound, - /// Failed to get an original for the given derivative. + /// Failed to get the derivative's extra data. + DerivativeExtraDataNotFound, + + /// Failed to get an original. OriginalNotFound, } - #[pallet::call] - impl, I: 'static> Pallet {} + #[pallet::call(weight(WeightInfoOf))] + impl, I: 'static> Pallet { + #[pallet::call_index(0)] + pub fn create_derivative( + origin: OriginFor, + original: OriginalOf, + derivative_create_params: DerivativeCreateParamsOf, + ) -> DispatchResult { + >::ensure_origin(origin)?; + + let derivative = >::create(derivative_create_params)?; + + Self::try_register_derivative(&original, &derivative) + } + + #[pallet::call_index(1)] + pub fn destroy_derivative( + origin: OriginFor, + original: OriginalOf, + ) -> DispatchResult { + >::ensure_origin(origin)?; + + let derivative = >::get(&original) + .ok_or(Error::::NoDerivativeToDeregister)?; + + >::destroy(&derivative, JustDo::default())?; + + Self::try_deregister_derivative_of(&original) + } + } } impl, I: 'static> DerivativesRegistry, DerivativeOf> @@ -116,11 +170,12 @@ impl, I: 'static> DerivativesRegistry, DerivativeO Ok(()) } - fn try_deregister_derivative(derivative: &DerivativeOf) -> DispatchResult { - let original = >::take(&derivative) + fn try_deregister_derivative_of(original: &OriginalOf) -> DispatchResult { + let derivative = >::take(&original) .ok_or(Error::::NoDerivativeToDeregister)?; - >::remove(&original); + >::remove(&derivative); + >::remove(&derivative); Self::deposit_event(Event::::DerivativeDeregistered { original: original.clone(), @@ -154,3 +209,119 @@ impl, I: 'static> IterDerivativesRegistry, Derivat >::iter() } } + +impl, I: 'static> DerivativesExtra, DerivativeExtraOf> + for Pallet +{ + fn get_derivative_extra(derivative: &DerivativeOf) -> Option> { + >::get(derivative) + } + + fn set_derivative_extra( + derivative: &DerivativeOf, + extra: Option>, + ) -> DispatchResult { + ensure!( + >::contains_key(derivative), + Error::::DerivativeNotFound, + ); + + >::set(derivative, extra); + + Ok(()) + } +} + +pub trait WeightInfo { + fn create_derivative() -> Weight; + fn destroy_derivative() -> Weight; +} + +pub struct TestWeightInfo; +impl WeightInfo for TestWeightInfo { + fn create_derivative() -> Weight { + Weight::from_parts(100_000_000, 0) + } + + fn destroy_derivative() -> Weight { + Weight::from_parts(100_000_000, 0) + } +} + +pub struct ProhibitiveWeightInfo; +impl WeightInfo for ProhibitiveWeightInfo { + fn create_derivative() -> Weight { + Weight::MAX + } + + fn destroy_derivative() -> Weight { + Weight::MAX + } +} + +pub trait ExtrinsicsConfig { + type CreateOrigin: EnsureOrigin; + type DestroyOrigin: EnsureOrigin; + + type DerivativeCreateParams: Parameter + CreateStrategy; + type DerivativeOps: AssetDefinition + + Create + + Destroy; + + type WeightInfo: WeightInfo; +} + +impl ExtrinsicsConfig for () { + type CreateOrigin = EnsureNever<()>; + type DestroyOrigin = EnsureNever<()>; + + type DerivativeCreateParams = DerivativeEmptyParams; + type DerivativeOps = DerivativeAlwaysErrOps; + + type WeightInfo = ProhibitiveWeightInfo; +} + +#[derive(Encode, Decode, TypeInfo, RuntimeDebug, Clone, PartialEq, Eq)] +pub struct DerivativeEmptyParams(PhantomData); +impl CreateStrategy for DerivativeEmptyParams { + type Success = Derivative; +} + +pub struct DerivativeAlwaysErrOps(PhantomData); +impl AssetDefinition for DerivativeAlwaysErrOps { + type Id = Derivative; +} +impl Create for DerivativeAlwaysErrOps { + fn create(_strategy: S) -> Result { + Err(DispatchError::BadOrigin) + } +} +impl Destroy for DerivativeAlwaysErrOps { + fn destroy(_id: &Self::Id, _strategy: S) -> Result { + Err(DispatchError::BadOrigin) + } +} + +pub type ExtrinsicsConfigOf = >::ExtrinsicsConfig; +type RuntimeOriginOf = ::RuntimeOrigin; + +pub type CreateOriginOf = as ExtrinsicsConfig< + RuntimeOriginOf, + DerivativeOf, +>>::CreateOrigin; +pub type DestroyOriginOf = as ExtrinsicsConfig< + RuntimeOriginOf, + DerivativeOf, +>>::DestroyOrigin; +pub type DerivativeCreateParamsOf = as ExtrinsicsConfig< + RuntimeOriginOf, + DerivativeOf, +>>::DerivativeCreateParams; +pub type DerivativeOpsOf = as ExtrinsicsConfig< + RuntimeOriginOf, + DerivativeOf, +>>::DerivativeOps; +pub type WeightInfoOf = as ExtrinsicsConfig< + RuntimeOriginOf, + DerivativeOf, +>>::WeightInfo; diff --git a/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs b/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs index 98b64e9f4bcc3..1ef03eb757142 100644 --- a/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs +++ b/polkadot/xcm/xcm-builder/src/unique_instances/derivatives.rs @@ -18,7 +18,7 @@ use super::NonFungibleAsset; pub trait DerivativesRegistry { fn try_register_derivative(original: &Original, derivative: &Derivative) -> DispatchResult; - fn try_deregister_derivative(derivative: &Derivative) -> DispatchResult; + fn try_deregister_derivative_of(original: &Original) -> DispatchResult; fn get_derivative(original: &Original) -> Option; @@ -34,6 +34,13 @@ pub trait IterDerivativesRegistry { fn iter() -> impl Iterator; } +/// Derivatives extra data. +pub trait DerivativesExtra { + fn get_derivative_extra(derivative: &Derivative) -> Option; + + fn set_derivative_extra(derivative: &Derivative, extra: Option) -> DispatchResult; +} + /// The `MatchDerivativeInstances` is an XCM Matcher /// that uses a [`DerivativesRegistry`] to match the XCM identification of the original instance /// to a derivative instance. From f47c7412235b8b4b78a1a2c9af5163c38ee9897c Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Fri, 25 Oct 2024 18:44:44 +0200 Subject: [PATCH 40/41] fix: add derives for common create strategies --- substrate/frame/support/src/traits/tokens/asset_ops.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/substrate/frame/support/src/traits/tokens/asset_ops.rs b/substrate/frame/support/src/traits/tokens/asset_ops.rs index dbf767e169c9b..ad9593521176b 100644 --- a/substrate/frame/support/src/traits/tokens/asset_ops.rs +++ b/substrate/frame/support/src/traits/tokens/asset_ops.rs @@ -504,6 +504,7 @@ pub mod common_strategies { /// /// The [`Success`](CreateStrategy::Success) will contain /// the [reported ID](IdAssignment::ReportedId) of the ID assignment approach. + #[derive(RuntimeDebug, PartialEq, Eq, Clone, Encode, Decode, MaxEncodedLen, TypeInfo)] pub struct Owned { pub owner: Owner, pub id_assignment: Assignment, @@ -537,6 +538,7 @@ pub mod common_strategies { /// /// The [`Success`](CreateStrategy::Success) will contain /// the [reported ID](IdAssignment::ReportedId) of the ID assignment approach. + #[derive(RuntimeDebug, PartialEq, Eq, Clone, Encode, Decode, MaxEncodedLen, TypeInfo)] pub struct Adminable { pub owner: Account, pub admin: Account, From 90c9fd985ffa3ce56ab4646dd87e2f61b8e80aac Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Tue, 29 Oct 2024 14:07:11 +0100 Subject: [PATCH 41/41] refactor(pallet-derivatives): split derivative ops --- polkadot/xcm/pallet-derivatives/src/lib.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/polkadot/xcm/pallet-derivatives/src/lib.rs b/polkadot/xcm/pallet-derivatives/src/lib.rs index 6e78e5f65b7e2..7893c6630a820 100644 --- a/polkadot/xcm/pallet-derivatives/src/lib.rs +++ b/polkadot/xcm/pallet-derivatives/src/lib.rs @@ -125,7 +125,7 @@ pub mod pallet { ) -> DispatchResult { >::ensure_origin(origin)?; - let derivative = >::create(derivative_create_params)?; + let derivative = >::create(derivative_create_params)?; Self::try_register_derivative(&original, &derivative) } @@ -140,7 +140,7 @@ pub mod pallet { let derivative = >::get(&original) .ok_or(Error::::NoDerivativeToDeregister)?; - >::destroy(&derivative, JustDo::default())?; + >::destroy(&derivative, JustDo::default())?; Self::try_deregister_derivative_of(&original) } @@ -264,9 +264,8 @@ pub trait ExtrinsicsConfig { type DestroyOrigin: EnsureOrigin; type DerivativeCreateParams: Parameter + CreateStrategy; - type DerivativeOps: AssetDefinition - + Create - + Destroy; + type DerivativeCreateOp: Create; + type DerivativeDestroyOp: AssetDefinition + Destroy; type WeightInfo: WeightInfo; } @@ -276,7 +275,8 @@ impl ExtrinsicsConfig for () { type DestroyOrigin = EnsureNever<()>; type DerivativeCreateParams = DerivativeEmptyParams; - type DerivativeOps = DerivativeAlwaysErrOps; + type DerivativeCreateOp = DerivativeAlwaysErrOps; + type DerivativeDestroyOp = DerivativeAlwaysErrOps; type WeightInfo = ProhibitiveWeightInfo; } @@ -317,10 +317,14 @@ pub type DerivativeCreateParamsOf = as Extrinsic RuntimeOriginOf, DerivativeOf, >>::DerivativeCreateParams; -pub type DerivativeOpsOf = as ExtrinsicsConfig< +pub type DerivativeCreateOpOf = as ExtrinsicsConfig< RuntimeOriginOf, DerivativeOf, ->>::DerivativeOps; +>>::DerivativeCreateOp; +pub type DerivativeDestroyOpOf = as ExtrinsicsConfig< + RuntimeOriginOf, + DerivativeOf, +>>::DerivativeDestroyOp; pub type WeightInfoOf = as ExtrinsicsConfig< RuntimeOriginOf, DerivativeOf,