From 83a8ca65ed2783e0a5c8ebbf7d54db03c76867a9 Mon Sep 17 00:00:00 2001 From: PraetorP Date: Thu, 19 Oct 2023 10:44:49 +0000 Subject: [PATCH 1/9] poc(nft xtransfer): added adapter, pallete proto --- Cargo.dev.toml | 1 + xcm-support/Cargo.toml | 16 ++ xcm-support/src/lib.rs | 3 +- xcm-support/src/parity_adapters.rs | 416 +++++++++++++++++++++++++++++ xnft/Cargo.toml | 43 +++ xnft/README.md | 4 + xnft/src/common_functions.rs | 0 xnft/src/impl_matches.rs | 29 ++ xnft/src/impl_nonfungibles.rs | 206 ++++++++++++++ xnft/src/lib.rs | 119 +++++++++ xnft/src/types.rs | 13 + 11 files changed, 848 insertions(+), 2 deletions(-) create mode 100644 xcm-support/src/parity_adapters.rs create mode 100644 xnft/Cargo.toml create mode 100644 xnft/README.md create mode 100644 xnft/src/common_functions.rs create mode 100644 xnft/src/impl_matches.rs create mode 100644 xnft/src/impl_nonfungibles.rs create mode 100644 xnft/src/lib.rs create mode 100644 xnft/src/types.rs diff --git a/Cargo.dev.toml b/Cargo.dev.toml index bffa0dd42..ff41946a4 100644 --- a/Cargo.dev.toml +++ b/Cargo.dev.toml @@ -16,6 +16,7 @@ members = [ "rewards", "nft", "xcm", + "xnft", "xtokens", "xcm-support", "unknown-tokens", diff --git a/xcm-support/Cargo.toml b/xcm-support/Cargo.toml index 3151c8994..a513ab30f 100644 --- a/xcm-support/Cargo.toml +++ b/xcm-support/Cargo.toml @@ -9,16 +9,31 @@ edition = "2021" [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +scale-info = { version = "2.5.0", default-features = false, features = [ + "derive", +] } +<<<<<<< HEAD frame-support = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false , branch = "release-polkadot-v1.1.0" } sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false , branch = "release-polkadot-v1.1.0" } sp-std = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false , branch = "release-polkadot-v1.1.0" } xcm = { package = "staging-xcm", git = "https://github.com/paritytech/polkadot-sdk", default-features = false , branch = "release-polkadot-v1.1.0" } xcm-executor = { package = "staging-xcm-executor", git = "https://github.com/paritytech/polkadot-sdk", default-features = false , branch = "release-polkadot-v1.1.0" } +======= +frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v1.0.0" } +sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v1.0.0" } +sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v1.0.0" } + +xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v1.0.0" } +xcm-executor = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v1.0.0" } +xcm-builder = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v1.0.0" } +>>>>>>> 11659c0 (poc(nft xtransfer): added adapter, pallete proto) orml-traits = { path = "../traits", version = "0.4.1-dev", default-features = false } +log = { version = "0.4.17", default-features = false } + [features] default = ["std"] std = [ @@ -29,4 +44,5 @@ std = [ "sp-std/std", "xcm-executor/std", "xcm/std", + "xcm-builder/std", ] diff --git a/xcm-support/src/lib.rs b/xcm-support/src/lib.rs index 1b3ed2e69..07d2caa22 100644 --- a/xcm-support/src/lib.rs +++ b/xcm-support/src/lib.rs @@ -22,9 +22,8 @@ use xcm_executor::traits::MatchesFungible; use orml_traits::{location::Reserve, GetByKey}; pub use currency_adapter::{DepositToAlternative, MultiCurrencyAdapter, OnDepositFail}; - mod currency_adapter; - +pub mod parity_adapters; mod tests; /// A `MatchesFungible` implementation. It matches concrete fungible assets diff --git a/xcm-support/src/parity_adapters.rs b/xcm-support/src/parity_adapters.rs new file mode 100644 index 000000000..750a78dc4 --- /dev/null +++ b/xcm-support/src/parity_adapters.rs @@ -0,0 +1,416 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! Adapters to work with [`frame_support::traits::tokens::nonfungibles_v2`] +//! through XCM. +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::{ + ensure, + traits::{tokens::nonfungibles_v2, Get}, +}; +use sp_runtime::scale_info::TypeInfo; +use sp_std::{marker::PhantomData, prelude::*, result}; +use xcm::latest::prelude::*; +use xcm_builder::{AssetChecking, MintLocation}; +use xcm_executor::traits::{ConvertLocation, Error as MatchError, MatchesNonFungibles, TransactAsset}; + +const LOG_TARGET: &str = "xcm::nonfungibles_v2_adapter"; +/// Adapter for transferring non-fungible tokens (NFTs) using +/// [`nonfungibles_v2`]. +/// +/// This adapter facilitates the transfer of NFTs between different locations. +pub struct NonFungiblesV2TransferAdapter( + PhantomData<(Assets, Matcher, AccountIdConverter, AccountId)>, +); +impl< + Assets: nonfungibles_v2::Transfer, + Matcher: MatchesNonFungibles, + AccountIdConverter: ConvertLocation, + AccountId: Clone, // can't get away without it since `nonfungibles_v2` is generic over it. + > TransactAsset for NonFungiblesV2TransferAdapter +{ + fn transfer_asset( + what: &MultiAsset, + from: &MultiLocation, + to: &MultiLocation, + context: &XcmContext, + ) -> result::Result { + log::trace!( + target: LOG_TARGET, + "transfer_asset what: {:?}, from: {:?}, to: {:?}, context: {:?}", + what, + from, + to, + context, + ); + // Check we handle this asset. + let (class, instance) = Matcher::matches_nonfungibles(what)?; + let destination = AccountIdConverter::convert_location(to).ok_or(MatchError::AccountIdConversionFailed)?; + Assets::transfer(&class, &instance, &destination).map_err(|e| XcmError::FailedToTransactAsset(e.into()))?; + Ok(what.clone().into()) + } +} + +/// Adapter for mutating non-fungible tokens (NFTs) using [`nonfungibles_v2`]. +/// +/// This adapter provides functions to withdraw, deposit, check in and check out +/// non fungibles. +pub struct NonFungiblesV2MutateAdapter< + Assets, + Matcher, + AccountIdConverter, + AccountId, + CheckAsset, + CheckingAccount, + ItemConfig, +>( + PhantomData<( + Assets, + Matcher, + AccountIdConverter, + AccountId, + CheckAsset, + CheckingAccount, + ItemConfig, + )>, +) +where + ItemConfig: Default; + +impl< + Assets: nonfungibles_v2::Mutate, + Matcher: MatchesNonFungibles, + AccountIdConverter: ConvertLocation, + AccountId: Clone + Eq, // can't get away without it since `nonfungibles_v2` is generic over it. + CheckAsset: AssetChecking, + CheckingAccount: Get>, + ItemConfig: Default, + > NonFungiblesV2MutateAdapter +{ + fn can_accrue_checked(class: Assets::CollectionId, instance: Assets::ItemId) -> XcmResult { + ensure!(Assets::owner(&class, &instance).is_none(), XcmError::NotDepositable); + Ok(()) + } + fn can_reduce_checked(class: Assets::CollectionId, instance: Assets::ItemId) -> XcmResult { + if let Some(checking_account) = CheckingAccount::get() { + // This is an asset whose teleports we track. + let owner = Assets::owner(&class, &instance); + ensure!(owner == Some(checking_account), XcmError::NotWithdrawable); + ensure!(Assets::can_transfer(&class, &instance), XcmError::NotWithdrawable); + } + Ok(()) + } + fn accrue_checked(class: Assets::CollectionId, instance: Assets::ItemId) { + if let Some(checking_account) = CheckingAccount::get() { + let ok = Assets::mint_into(&class, &instance, &checking_account, &ItemConfig::default(), true).is_ok(); + debug_assert!(ok, "`mint_into` cannot generally fail; qed"); + } + } + fn reduce_checked(class: Assets::CollectionId, instance: Assets::ItemId) { + let ok = Assets::burn(&class, &instance, None).is_ok(); + debug_assert!(ok, "`can_check_in` must have returned `true` immediately prior; qed"); + } +} + +impl< + Assets: nonfungibles_v2::Mutate, + Matcher: MatchesNonFungibles, + AccountIdConverter: ConvertLocation, + AccountId: Clone + Eq, // can't get away without it since `nonfungibles_v2` is generic over it. + CheckAsset: AssetChecking, + CheckingAccount: Get>, + ItemConfig: Default, + > TransactAsset + for NonFungiblesV2MutateAdapter +{ + fn can_check_in(_origin: &MultiLocation, what: &MultiAsset, context: &XcmContext) -> XcmResult { + log::trace!( + target: LOG_TARGET, + "can_check_in origin: {:?}, what: {:?}, context: {:?}", + _origin, + what, + context, + ); + // Check we handle this asset. + let (class, instance) = Matcher::matches_nonfungibles(what)?; + match CheckAsset::asset_checking(&class) { + // We track this asset's teleports to ensure no more come in than have gone out. + Some(MintLocation::Local) => Self::can_reduce_checked(class, instance), + // We track this asset's teleports to ensure no more go out than have come in. + Some(MintLocation::NonLocal) => Self::can_accrue_checked(class, instance), + _ => Ok(()), + } + } + + fn check_in(_origin: &MultiLocation, what: &MultiAsset, context: &XcmContext) { + log::trace!( + target: LOG_TARGET, + "check_in origin: {:?}, what: {:?}, context: {:?}", + _origin, + what, + context, + ); + if let Ok((class, instance)) = Matcher::matches_nonfungibles(what) { + match CheckAsset::asset_checking(&class) { + // We track this asset's teleports to ensure no more come in than have gone out. + Some(MintLocation::Local) => Self::reduce_checked(class, instance), + // We track this asset's teleports to ensure no more go out than have come in. + Some(MintLocation::NonLocal) => Self::accrue_checked(class, instance), + _ => (), + } + } + } + + fn can_check_out(_dest: &MultiLocation, what: &MultiAsset, context: &XcmContext) -> XcmResult { + log::trace!( + target: LOG_TARGET, + "can_check_out dest: {:?}, what: {:?}, context: {:?}", + _dest, + what, + context, + ); + // Check we handle this asset. + let (class, instance) = Matcher::matches_nonfungibles(what)?; + match CheckAsset::asset_checking(&class) { + // We track this asset's teleports to ensure no more come in than have gone out. + Some(MintLocation::Local) => Self::can_accrue_checked(class, instance), + // We track this asset's teleports to ensure no more go out than have come in. + Some(MintLocation::NonLocal) => Self::can_reduce_checked(class, instance), + _ => Ok(()), + } + } + + fn check_out(_dest: &MultiLocation, what: &MultiAsset, context: &XcmContext) { + log::trace!( + target: LOG_TARGET, + "check_out dest: {:?}, what: {:?}, context: {:?}", + _dest, + what, + context, + ); + if let Ok((class, instance)) = Matcher::matches_nonfungibles(what) { + match CheckAsset::asset_checking(&class) { + // We track this asset's teleports to ensure no more come in than have gone out. + Some(MintLocation::Local) => Self::accrue_checked(class, instance), + // We track this asset's teleports to ensure no more go out than have come in. + Some(MintLocation::NonLocal) => Self::reduce_checked(class, instance), + _ => (), + } + } + } + + fn deposit_asset(what: &MultiAsset, who: &MultiLocation, context: &XcmContext) -> XcmResult { + log::trace!( + target: LOG_TARGET, + "deposit_asset what: {:?}, who: {:?}, context: {:?}", + what, + who, + context, + ); + // Check we handle this asset. + let (class, instance) = Matcher::matches_nonfungibles(what)?; + let who = AccountIdConverter::convert_location(who).ok_or(MatchError::AccountIdConversionFailed)?; + + Assets::mint_into(&class, &instance, &who, &ItemConfig::default(), true) + .map_err(|e| XcmError::FailedToTransactAsset(e.into())) + } + + fn withdraw_asset( + what: &MultiAsset, + who: &MultiLocation, + maybe_context: Option<&XcmContext>, + ) -> result::Result { + log::trace!( + target: LOG_TARGET, + "withdraw_asset what: {:?}, who: {:?}, maybe_context: {:?}", + what, + who, + maybe_context, + ); + // Check we handle this asset. + let who = AccountIdConverter::convert_location(who).ok_or(MatchError::AccountIdConversionFailed)?; + let (class, instance) = Matcher::matches_nonfungibles(what)?; + Assets::burn(&class, &instance, Some(&who)).map_err(|e| XcmError::FailedToTransactAsset(e.into()))?; + Ok(what.clone().into()) + } +} + +/// Adapter for handling non-fungible tokens (NFTs) using [`nonfungibles_v2`]. +/// +/// This adapter combines the functionalities of both the +/// [`NonFungiblesV2TransferAdapter`] and [`NonFungiblesV2MutateAdapter`] +/// adapters, allowing handling NFTs in various scenarios. +/// For detailed information on the functions, refer to [`TransactAsset`]. +pub struct NonFungiblesV2Adapter< + Assets, + Matcher, + AccountIdConverter, + AccountId, + CheckAsset, + CheckingAccount, + ItemConfig, +>( + PhantomData<( + Assets, + Matcher, + AccountIdConverter, + AccountId, + CheckAsset, + CheckingAccount, + ItemConfig, + )>, +) +where + ItemConfig: Default; +impl< + Assets: nonfungibles_v2::Mutate + nonfungibles_v2::Transfer, + Matcher: MatchesNonFungibles, + AccountIdConverter: ConvertLocation, + AccountId: Clone + Eq, // can't get away without it since `nonfungibles_v2` is generic over it. + CheckAsset: AssetChecking, + CheckingAccount: Get>, + ItemConfig: Default, + > TransactAsset + for NonFungiblesV2Adapter +{ + fn can_check_in(origin: &MultiLocation, what: &MultiAsset, context: &XcmContext) -> XcmResult { + NonFungiblesV2MutateAdapter::< + Assets, + Matcher, + AccountIdConverter, + AccountId, + CheckAsset, + CheckingAccount, + ItemConfig, + >::can_check_in(origin, what, context) + } + + fn check_in(origin: &MultiLocation, what: &MultiAsset, context: &XcmContext) { + NonFungiblesV2MutateAdapter::< + Assets, + Matcher, + AccountIdConverter, + AccountId, + CheckAsset, + CheckingAccount, + ItemConfig, + >::check_in(origin, what, context) + } + + fn can_check_out(dest: &MultiLocation, what: &MultiAsset, context: &XcmContext) -> XcmResult { + NonFungiblesV2MutateAdapter::< + Assets, + Matcher, + AccountIdConverter, + AccountId, + CheckAsset, + CheckingAccount, + ItemConfig, + >::can_check_out(dest, what, context) + } + + fn check_out(dest: &MultiLocation, what: &MultiAsset, context: &XcmContext) { + NonFungiblesV2MutateAdapter::< + Assets, + Matcher, + AccountIdConverter, + AccountId, + CheckAsset, + CheckingAccount, + ItemConfig, + >::check_out(dest, what, context) + } + + fn deposit_asset(what: &MultiAsset, who: &MultiLocation, context: &XcmContext) -> XcmResult { + NonFungiblesV2MutateAdapter::< + Assets, + Matcher, + AccountIdConverter, + AccountId, + CheckAsset, + CheckingAccount, + ItemConfig, + >::deposit_asset(what, who, context) + } + + fn withdraw_asset( + what: &MultiAsset, + who: &MultiLocation, + maybe_context: Option<&XcmContext>, + ) -> result::Result { + NonFungiblesV2MutateAdapter::< + Assets, + Matcher, + AccountIdConverter, + AccountId, + CheckAsset, + CheckingAccount, + ItemConfig, + >::withdraw_asset(what, who, maybe_context) + } + + fn transfer_asset( + what: &MultiAsset, + from: &MultiLocation, + to: &MultiLocation, + context: &XcmContext, + ) -> result::Result { + NonFungiblesV2TransferAdapter::::transfer_asset( + what, from, to, context, + ) + } +} + +#[derive(Copy, Clone, Decode, Encode, Eq, PartialEq, Ord, PartialOrd, Debug, TypeInfo, MaxEncodedLen)] +/// Represents a collection ID based on a MultiLocation. +/// +/// This structure provides a way to map a MultiLocation to a collection ID, +/// which is useful for describing collections that do not follow an incremental +/// pattern. +pub struct MultiLocationCollectionId(MultiLocation); +impl MultiLocationCollectionId { + /// Consume `self` and return the inner MultiLocation. + pub fn into_inner(self) -> MultiLocation { + self.0 + } + + /// Return a reference to the inner MultiLocation. + pub fn inner(&self) -> &MultiLocation { + &self.0 + } +} + +// impl Incrementable for MultiLocationCollectionId { +// fn increment(&self) -> Option { +// None +// } + +// fn initial_value() -> Option { +// None +// } +// } + +impl From for MultiLocationCollectionId { + fn from(value: MultiLocation) -> Self { + MultiLocationCollectionId(value) + } +} + +impl From for MultiLocation { + fn from(value: MultiLocationCollectionId) -> MultiLocation { + value.into_inner() + } +} diff --git a/xnft/Cargo.toml b/xnft/Cargo.toml new file mode 100644 index 000000000..4caa24669 --- /dev/null +++ b/xnft/Cargo.toml @@ -0,0 +1,43 @@ +[package] +name = "orml-xnft" +description = "POC" +repository = "https://github.com/open-web3-stack/open-runtime-module-library/tree/master/xnft" +license = "Apache-2.0" +version = "0.4.1-dev" +authors = ["Unique Developers"] +edition = "2021" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ + "max-encoded-len", +] } +scale-info = { version = "2.9.0", default-features = false, features = [ + "derive", +] } +serde = { version = "1.0.136", optional = true } + +frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v1.0.0" } +frame-system = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v1.0.0" } +sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v1.0.0" } +sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v1.0.0" } + +xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v1.0.0" } +xcm-executor = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v1.0.0" } + +[dev-dependencies] +sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0" } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0" } + +[features] +default = ["std"] +std = [ + "serde", + "codec/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "sp-runtime/std", + "sp-std/std", + "xcm-executor/std", +] +try-runtime = ["frame-support/try-runtime", "frame-system/try-runtime"] diff --git a/xnft/README.md b/xnft/README.md new file mode 100644 index 000000000..3f4ee815a --- /dev/null +++ b/xnft/README.md @@ -0,0 +1,4 @@ +# XCM non-fungible-token module + +### Overview + diff --git a/xnft/src/common_functions.rs b/xnft/src/common_functions.rs new file mode 100644 index 000000000..e69de29bb diff --git a/xnft/src/impl_matches.rs b/xnft/src/impl_matches.rs new file mode 100644 index 000000000..e861fc05d --- /dev/null +++ b/xnft/src/impl_matches.rs @@ -0,0 +1,29 @@ +use crate::*; +use frame_support::traits::Incrementable; +use xcm_executor::traits::{Error as MatchError, MatchesNonFungibles}; + +impl MatchesNonFungibles, ItemIdOf> for Pallet +where + ItemIdOf: MaxEncodedLen + Incrementable, + CollectionIdOf: MaxEncodedLen, +{ + fn matches_nonfungibles( + foreign_asset: &MultiAsset, + ) -> core::result::Result<(CollectionIdOf, ItemIdOf), MatchError> { + let asset = Self::assets(foreign_asset.id).ok_or(MatchError::AssetNotHandled)?; + let item = Self::items(&asset, &foreign_asset.fun).unwrap_or(Self::get_next_item_of(&asset)?); + Ok((asset, item)) + } +} + +impl Pallet +where + ItemIdOf: MaxEncodedLen + Incrementable, + CollectionIdOf: MaxEncodedLen, +{ + pub fn get_next_item_of(collection_id: &CollectionIdOf) -> Result, MatchError> { + let item = >::get(collection_id).unwrap_or(>::initial_value()); + >::set(collection_id, Some(item.increment())); + Ok(item) + } +} diff --git a/xnft/src/impl_nonfungibles.rs b/xnft/src/impl_nonfungibles.rs new file mode 100644 index 000000000..1e36259be --- /dev/null +++ b/xnft/src/impl_nonfungibles.rs @@ -0,0 +1,206 @@ +use crate::*; +pub(crate) type ExecutorOf = ::NftExecutor; + +impl Inspect for Pallet +where + ItemIdOf: MaxEncodedLen, + CollectionIdOf: MaxEncodedLen, +{ + type ItemId = ItemIdOf; + + type CollectionId = CollectionIdOf; + + fn owner(collection: &Self::CollectionId, item: &Self::ItemId) -> Option { + >::owner(collection, item) + } + + fn collection_owner(collection: &Self::CollectionId) -> Option { + >::collection_owner(collection) + } + + fn attribute(collection: &Self::CollectionId, item: &Self::ItemId, key: &[u8]) -> Option> { + >::attribute(collection, item, key) + } + + fn custom_attribute( + account: &T::AccountId, + collection: &Self::CollectionId, + item: &Self::ItemId, + key: &[u8], + ) -> Option> { + >::custom_attribute(account, collection, item, key) + } + + fn system_attribute(collection: &Self::CollectionId, item: &Self::ItemId, key: &[u8]) -> Option> { + >::system_attribute(collection, item, key) + } + + fn typed_attribute( + collection: &Self::CollectionId, + item: &Self::ItemId, + key: &K, + ) -> Option { + >::typed_attribute(collection, item, key) + } + + fn typed_custom_attribute( + account: &T::AccountId, + collection: &Self::CollectionId, + item: &Self::ItemId, + key: &K, + ) -> Option { + >::typed_custom_attribute(account, collection, item, key) + } + + fn typed_system_attribute( + collection: &Self::CollectionId, + item: &Self::ItemId, + key: &K, + ) -> Option { + >::typed_system_attribute(collection, item, key) + } + + fn collection_attribute(collection: &Self::CollectionId, key: &[u8]) -> Option> { + >::collection_attribute(collection, key) + } + + fn typed_collection_attribute(collection: &Self::CollectionId, key: &K) -> Option { + >::typed_collection_attribute(collection, key) + } + + fn can_transfer(collection: &Self::CollectionId, item: &Self::ItemId) -> bool { + >::can_transfer(collection, item) + } +} + +impl Transfer for Pallet +where + ItemIdOf: MaxEncodedLen, + CollectionIdOf: MaxEncodedLen, + // ItemId: MaxEncodedLen + Parameter, + // CollectionId: MaxEncodedLen + Parameter, +{ + fn disable_transfer( + collection: &Self::CollectionId, + item: &Self::ItemId, + ) -> frame_support::pallet_prelude::DispatchResult { + >::disable_transfer(collection, item) + } + + fn enable_transfer( + collection: &Self::CollectionId, + item: &Self::ItemId, + ) -> frame_support::pallet_prelude::DispatchResult { + >::enable_transfer(collection, item) + } + + fn transfer( + collection: &Self::CollectionId, + item: &Self::ItemId, + destination: &T::AccountId, + ) -> frame_support::pallet_prelude::DispatchResult { + >::transfer(collection, item, destination) + } +} + +impl Mutate for Pallet +where + ItemIdOf: MaxEncodedLen, + CollectionIdOf: MaxEncodedLen, +{ + fn mint_into( + collection: &Self::CollectionId, + item: &Self::ItemId, + who: &T::AccountId, + config: &T::ItemConfig, + deposit_collection_owner: bool, + ) -> frame_support::pallet_prelude::DispatchResult { + >::mint_into(collection, item, who, config, deposit_collection_owner) + } + + fn burn( + collection: &Self::CollectionId, + item: &Self::ItemId, + maybe_check_owner: Option<&T::AccountId>, + ) -> frame_support::pallet_prelude::DispatchResult { + >::burn(collection, item, maybe_check_owner) + } + + fn set_attribute( + collection: &Self::CollectionId, + item: &Self::ItemId, + key: &[u8], + value: &[u8], + ) -> frame_support::pallet_prelude::DispatchResult { + >::set_attribute(collection, item, key, value) + } + + fn set_typed_attribute( + collection: &Self::CollectionId, + item: &Self::ItemId, + key: &K, + value: &V, + ) -> frame_support::pallet_prelude::DispatchResult { + >::set_typed_attribute(collection, item, key, value) + } + + fn set_collection_attribute( + collection: &Self::CollectionId, + key: &[u8], + value: &[u8], + ) -> frame_support::pallet_prelude::DispatchResult { + >::set_collection_attribute(collection, key, value) + } + + fn set_typed_collection_attribute( + collection: &Self::CollectionId, + key: &K, + value: &V, + ) -> frame_support::pallet_prelude::DispatchResult { + >::set_typed_collection_attribute(collection, key, value) + } + + fn clear_attribute( + collection: &Self::CollectionId, + item: &Self::ItemId, + key: &[u8], + ) -> frame_support::pallet_prelude::DispatchResult { + >::clear_attribute(collection, item, key) + } + + fn clear_typed_attribute( + collection: &Self::CollectionId, + item: &Self::ItemId, + key: &K, + ) -> frame_support::pallet_prelude::DispatchResult { + >::clear_typed_attribute(collection, item, key) + } + + fn clear_collection_attribute( + collection: &Self::CollectionId, + key: &[u8], + ) -> frame_support::pallet_prelude::DispatchResult { + >::clear_collection_attribute(collection, key) + } + + fn clear_typed_collection_attribute( + collection: &Self::CollectionId, + key: &K, + ) -> frame_support::pallet_prelude::DispatchResult { + >::clear_typed_collection_attribute(collection, key) + } +} + +impl Create for Pallet +where + ItemIdOf: MaxEncodedLen, + CollectionIdOf: MaxEncodedLen, +{ + fn create_collection( + who: &T::AccountId, + admin: &T::AccountId, + config: &T::CollectionConfig, + ) -> Result { + >::create_collection(who, admin, config) + } +} diff --git a/xnft/src/lib.rs b/xnft/src/lib.rs new file mode 100644 index 000000000..c843a177f --- /dev/null +++ b/xnft/src/lib.rs @@ -0,0 +1,119 @@ +#![cfg_attr(not(feature = "std"), no_std)] +#![allow(clippy::unused_unit)] + +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::{ + ensure, + pallet_prelude::*, + traits::tokens::nonfungibles_v2::{Create, Inspect, Mutate, Transfer}, +}; +use frame_system::pallet_prelude::*; +use frame_system::Config as SystemConfig; +use scale_info::TypeInfo; +use sp_runtime::{traits::AccountIdConversion, DispatchError, DispatchResult, RuntimeDebug}; +use sp_std::vec::Vec; +use xcm::v3::{AssetId, MultiAsset}; + +pub mod impl_matches; +pub mod impl_nonfungibles; +pub mod types; +pub use pallet::*; +pub(crate) use types::*; + +#[frame_support::pallet] +pub mod pallet { + use xcm::v3::Fungibility; + + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config + where + ItemIdOf: MaxEncodedLen, + CollectionIdOf: MaxEncodedLen, + { + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + type NftExecutor: Create + + Mutate + + Transfer; + + type ItemConfig: Default; + + type CollectionConfig: Default; + } + + /// Error for non-fungible-token module. + #[pallet::error] + pub enum Error { + AssetAlreadyRegistered, + } + + #[pallet::event] + #[pallet::generate_deposit(pub(crate) fn deposit_event)] + pub enum Event + where + ItemIdOf: MaxEncodedLen, + CollectionIdOf: MaxEncodedLen, + { + RegisteredAsset { + asset_id: AssetId, + collection_id: CollectionIdOf, + }, + } + + #[pallet::storage] + #[pallet::getter(fn local_assets)] + pub type LocalAssets = + StorageMap<_, Twox64Concat, MultiAsset, LocalAsset, ItemIdOf>, OptionQuery>; + + #[pallet::storage] + #[pallet::getter(fn assets)] + pub type AssetsMapping = StorageMap<_, Twox64Concat, AssetId, CollectionIdOf, OptionQuery>; + + #[pallet::storage] + #[pallet::getter(fn items)] + pub type ItemsMapping = + StorageDoubleMap<_, Twox64Concat, CollectionIdOf, Twox64Concat, Fungibility, ItemIdOf, OptionQuery>; + + #[pallet::storage] + pub type NextItemId = StorageMap<_, Twox64Concat, CollectionIdOf, ItemIdOf, OptionQuery>; + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::call] + impl Pallet + where + ItemIdOf: MaxEncodedLen + Default, + CollectionIdOf: MaxEncodedLen, + { + #[pallet::call_index(0)] + #[pallet::weight(0)] + pub fn register_asset(origin: OriginFor, foreign_asset: Box) -> DispatchResult { + ensure_signed(origin)?; + ensure!( + !>::contains_key(foreign_asset.as_ref()), + >::AssetAlreadyRegistered, + ); + let collection_id = + >::create_collection(&Self::account_id(), &Self::account_id(), &Default::default())?; + >::insert(foreign_asset.as_ref(), collection_id.clone()); + Self::deposit_event(Event::RegisteredAsset { + asset_id: *foreign_asset, + collection_id, + }); + Ok(()) + } + } +} + +impl Pallet +where + ItemIdOf: MaxEncodedLen + Default, + CollectionIdOf: MaxEncodedLen, +{ + pub fn account_id() -> T::AccountId { + frame_support::PalletId(*b"poc_xnft").into_account_truncating() + } +} diff --git a/xnft/src/types.rs b/xnft/src/types.rs new file mode 100644 index 000000000..c02d346d6 --- /dev/null +++ b/xnft/src/types.rs @@ -0,0 +1,13 @@ +use crate::*; + +pub type ItemIdOf = <::NftExecutor as Inspect<::AccountId>>::ItemId; + +pub type CollectionIdOf = <::NftExecutor as Inspect<::AccountId>>::CollectionId; + +pub type ExecutorOf = ::NftExecutor; + +#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] +pub struct LocalAsset { + pub collection_id: CollectionId, + pub item_id: ItemId, +} From f13f7502c91d38c0cd066019f8a308cb88caae60 Mon Sep 17 00:00:00 2001 From: PraetorP Date: Tue, 24 Oct 2023 10:06:38 +0000 Subject: [PATCH 2/9] fix sp_std --- xnft/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xnft/src/lib.rs b/xnft/src/lib.rs index c843a177f..cdbb35fbf 100644 --- a/xnft/src/lib.rs +++ b/xnft/src/lib.rs @@ -11,7 +11,7 @@ use frame_system::pallet_prelude::*; use frame_system::Config as SystemConfig; use scale_info::TypeInfo; use sp_runtime::{traits::AccountIdConversion, DispatchError, DispatchResult, RuntimeDebug}; -use sp_std::vec::Vec; +use sp_std::{boxed::Box, vec::Vec}; use xcm::v3::{AssetId, MultiAsset}; pub mod impl_matches; From 88c3e36e17178f51d5cb4ec47c8c25dcacb40f42 Mon Sep 17 00:00:00 2001 From: PraetorP Date: Tue, 24 Oct 2023 14:16:38 +0000 Subject: [PATCH 3/9] fix cargo --- xcm-support/Cargo.toml | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/xcm-support/Cargo.toml b/xcm-support/Cargo.toml index a513ab30f..e5009140d 100644 --- a/xcm-support/Cargo.toml +++ b/xcm-support/Cargo.toml @@ -13,22 +13,13 @@ scale-info = { version = "2.5.0", default-features = false, features = [ "derive", ] } -<<<<<<< HEAD -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false , branch = "release-polkadot-v1.1.0" } -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false , branch = "release-polkadot-v1.1.0" } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false , branch = "release-polkadot-v1.1.0" } +frame-support = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.1.0" } +sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.1.0" } +sp-std = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.1.0" } -xcm = { package = "staging-xcm", git = "https://github.com/paritytech/polkadot-sdk", default-features = false , branch = "release-polkadot-v1.1.0" } -xcm-executor = { package = "staging-xcm-executor", git = "https://github.com/paritytech/polkadot-sdk", default-features = false , branch = "release-polkadot-v1.1.0" } -======= -frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v1.0.0" } -sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v1.0.0" } -sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v1.0.0" } - -xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v1.0.0" } -xcm-executor = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v1.0.0" } -xcm-builder = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v1.0.0" } ->>>>>>> 11659c0 (poc(nft xtransfer): added adapter, pallete proto) +xcm = { package = "staging-xcm", git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.1.0" } +xcm-executor = { package = "staging-xcm-executor", git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.1.0" } +xcm-builder = { package = "staging-xcm-builder", git = "https://github.com/paritytech/polkadot-sdk", default-features = false, branch = "release-polkadot-v1.1.0" } orml-traits = { path = "../traits", version = "0.4.1-dev", default-features = false } From 666e1b5efe5696c574356967f2b00d6e1d32bd17 Mon Sep 17 00:00:00 2001 From: PraetorP Date: Tue, 24 Oct 2023 16:34:15 +0000 Subject: [PATCH 4/9] feat(poc): migrate `xnft` to 1.1.0 --- xnft/Cargo.toml | 17 +++++++++-------- xnft/src/impl_matches.rs | 8 ++++++-- xnft/src/impl_nonfungibles.rs | 9 +++++++++ 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/xnft/Cargo.toml b/xnft/Cargo.toml index 4caa24669..0137e51b0 100644 --- a/xnft/Cargo.toml +++ b/xnft/Cargo.toml @@ -16,17 +16,17 @@ scale-info = { version = "2.9.0", default-features = false, features = [ ] } serde = { version = "1.0.136", optional = true } -frame-support = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v1.0.0" } -frame-system = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v1.0.0" } -sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v1.0.0" } -sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v1.0.0" } +frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +frame-system = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } -xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v1.0.0" } -xcm-executor = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v1.0.0" } +xcm = { package = "staging-xcm", git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +xcm-executor = { package = "staging-xcm-executor", git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } [dev-dependencies] -sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0" } -sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0" } +sp-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } +sp-io = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } [features] default = ["std"] @@ -39,5 +39,6 @@ std = [ "sp-runtime/std", "sp-std/std", "xcm-executor/std", + "xcm/std", ] try-runtime = ["frame-support/try-runtime", "frame-system/try-runtime"] diff --git a/xnft/src/impl_matches.rs b/xnft/src/impl_matches.rs index e861fc05d..8740d9f36 100644 --- a/xnft/src/impl_matches.rs +++ b/xnft/src/impl_matches.rs @@ -22,8 +22,12 @@ where CollectionIdOf: MaxEncodedLen, { pub fn get_next_item_of(collection_id: &CollectionIdOf) -> Result, MatchError> { - let item = >::get(collection_id).unwrap_or(>::initial_value()); - >::set(collection_id, Some(item.increment())); + let item = >::get(collection_id) + .unwrap_or(>::initial_value().ok_or(MatchError::InstanceConversionFailed)?); + >::set( + collection_id, + Some(item.increment().ok_or(MatchError::InstanceConversionFailed)?), + ); Ok(item) } } diff --git a/xnft/src/impl_nonfungibles.rs b/xnft/src/impl_nonfungibles.rs index 1e36259be..e8b43790d 100644 --- a/xnft/src/impl_nonfungibles.rs +++ b/xnft/src/impl_nonfungibles.rs @@ -203,4 +203,13 @@ where ) -> Result { >::create_collection(who, admin, config) } + + fn create_collection_with_id( + collection: Self::CollectionId, + who: &T::AccountId, + admin: &T::AccountId, + config: &T::CollectionConfig, + ) -> Result<(), DispatchError> { + >::create_collection_with_id(collection, who, admin, config) + } } From fbfb45ea44de5600a7bf58749c2d0608b79535ee Mon Sep 17 00:00:00 2001 From: PraetorP Date: Thu, 26 Oct 2023 05:02:17 +0000 Subject: [PATCH 5/9] update deps --- xcm-support/Cargo.toml | 2 +- xnft/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/xcm-support/Cargo.toml b/xcm-support/Cargo.toml index e5009140d..33f254ffe 100644 --- a/xcm-support/Cargo.toml +++ b/xcm-support/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } -scale-info = { version = "2.5.0", default-features = false, features = [ +scale-info = { version = "2.9.0", default-features = false, features = [ "derive", ] } diff --git a/xnft/Cargo.toml b/xnft/Cargo.toml index 0137e51b0..66fc01c8e 100644 --- a/xnft/Cargo.toml +++ b/xnft/Cargo.toml @@ -35,8 +35,8 @@ std = [ "codec/std", "frame-support/std", "frame-system/std", - "scale-info/std", "sp-runtime/std", + "scale-info/std", "sp-std/std", "xcm-executor/std", "xcm/std", From a8a4f3c0e749ee801125995b7177c8c7702965e5 Mon Sep 17 00:00:00 2001 From: PraetorP Date: Thu, 26 Oct 2023 12:01:01 +0000 Subject: [PATCH 6/9] fix deps --- xnft/Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xnft/Cargo.toml b/xnft/Cargo.toml index 66fc01c8e..067b89ada 100644 --- a/xnft/Cargo.toml +++ b/xnft/Cargo.toml @@ -22,7 +22,7 @@ sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "rel sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } xcm = { package = "staging-xcm", git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } -xcm-executor = { package = "staging-xcm-executor", git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } +xcm-executor = { package = "staging-xcm-executor", git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } [dev-dependencies] sp-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } @@ -32,6 +32,7 @@ sp-io = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release- default = ["std"] std = [ "serde", + "codec/std", "frame-support/std", "frame-system/std", From 88cbfc43d01b6e2981cf1043b4572e67335fc03e Mon Sep 17 00:00:00 2001 From: PraetorP Date: Fri, 27 Oct 2023 11:50:27 +0000 Subject: [PATCH 7/9] refactor: types --- xnft/src/common_functions.rs | 0 xnft/src/impl_nonfungibles.rs | 1 - xnft/src/lib.rs | 5 ----- xnft/src/types.rs | 6 ------ 4 files changed, 12 deletions(-) delete mode 100644 xnft/src/common_functions.rs diff --git a/xnft/src/common_functions.rs b/xnft/src/common_functions.rs deleted file mode 100644 index e69de29bb..000000000 diff --git a/xnft/src/impl_nonfungibles.rs b/xnft/src/impl_nonfungibles.rs index e8b43790d..0a1ae6571 100644 --- a/xnft/src/impl_nonfungibles.rs +++ b/xnft/src/impl_nonfungibles.rs @@ -1,5 +1,4 @@ use crate::*; -pub(crate) type ExecutorOf = ::NftExecutor; impl Inspect for Pallet where diff --git a/xnft/src/lib.rs b/xnft/src/lib.rs index cdbb35fbf..b9d0c4e14 100644 --- a/xnft/src/lib.rs +++ b/xnft/src/lib.rs @@ -62,11 +62,6 @@ pub mod pallet { }, } - #[pallet::storage] - #[pallet::getter(fn local_assets)] - pub type LocalAssets = - StorageMap<_, Twox64Concat, MultiAsset, LocalAsset, ItemIdOf>, OptionQuery>; - #[pallet::storage] #[pallet::getter(fn assets)] pub type AssetsMapping = StorageMap<_, Twox64Concat, AssetId, CollectionIdOf, OptionQuery>; diff --git a/xnft/src/types.rs b/xnft/src/types.rs index c02d346d6..6cc2dc98f 100644 --- a/xnft/src/types.rs +++ b/xnft/src/types.rs @@ -5,9 +5,3 @@ pub type ItemIdOf = <::NftExecutor as Inspect< = <::NftExecutor as Inspect<::AccountId>>::CollectionId; pub type ExecutorOf = ::NftExecutor; - -#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)] -pub struct LocalAsset { - pub collection_id: CollectionId, - pub item_id: ItemId, -} From 36fe63bfb0c02dca5cb5bf54d413643c87e7fb91 Mon Sep 17 00:00:00 2001 From: Daniel Shiposha Date: Fri, 27 Oct 2023 17:47:28 +0200 Subject: [PATCH 8/9] feat: support nfts in xtokens --- xtokens/src/lib.rs | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/xtokens/src/lib.rs b/xtokens/src/lib.rs index fe479f27a..4d535717d 100644 --- a/xtokens/src/lib.rs +++ b/xtokens/src/lib.rs @@ -526,20 +526,34 @@ pub mod module { let asset_len = assets.len(); for i in 0..asset_len { let asset = assets.get(i).ok_or(Error::::AssetIndexNonExistent)?; - ensure!( - matches!(asset.fun, Fungibility::Fungible(x) if !x.is_zero()), - Error::::InvalidAsset - ); - // `assets` includes fee, the reserve location is decided by non fee asset - if (fee != *asset && non_fee_reserve.is_none()) || asset_len == 1 { - non_fee_reserve = T::ReserveProvider::reserve(asset); - } - // make sure all non fee assets share the same reserve - if non_fee_reserve.is_some() { + + if fee == *asset { + // Fee payment can only be made by using fungibles ensure!( - non_fee_reserve == T::ReserveProvider::reserve(asset), - Error::::DistinctReserveForAssetAndFee + matches!(asset.fun, Fungibility::Fungible(x) if !x.is_zero()), + Error::::InvalidAsset ); + } else { + match asset.fun { + Fungibility::Fungible(x) => ensure!(!x.is_zero(), Error::::InvalidAsset), + Fungibility::NonFungible(AssetInstance::Undefined) => { + return Err(Error::::InvalidAsset.into()) + } + _ => {} + } + + // `assets` includes fee, the reserve location is decided by non fee asset + if non_fee_reserve.is_none() || asset_len == 1 { + non_fee_reserve = T::ReserveProvider::reserve(asset); + } + + // make sure all non fee assets share the same reserve + if non_fee_reserve.is_some() { + ensure!( + non_fee_reserve == T::ReserveProvider::reserve(asset), + Error::::DistinctReserveForAssetAndFee + ); + } } } From 1ac58ab4b91654018ccdbcfdb4fa182de6a93c92 Mon Sep 17 00:00:00 2001 From: PraetorP Date: Mon, 30 Oct 2023 12:38:38 +0000 Subject: [PATCH 9/9] trefactor --- xnft/src/impl_matches.rs | 6 +++++- xnft/src/lib.rs | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/xnft/src/impl_matches.rs b/xnft/src/impl_matches.rs index 8740d9f36..a41863572 100644 --- a/xnft/src/impl_matches.rs +++ b/xnft/src/impl_matches.rs @@ -1,5 +1,6 @@ use crate::*; use frame_support::traits::Incrementable; +use xcm::v3::Fungibility; use xcm_executor::traits::{Error as MatchError, MatchesNonFungibles}; impl MatchesNonFungibles, ItemIdOf> for Pallet @@ -10,8 +11,11 @@ where fn matches_nonfungibles( foreign_asset: &MultiAsset, ) -> core::result::Result<(CollectionIdOf, ItemIdOf), MatchError> { + let Fungibility::NonFungible(asset_instance) = foreign_asset.fun else { + return Err(MatchError::AssetNotHandled); + }; let asset = Self::assets(foreign_asset.id).ok_or(MatchError::AssetNotHandled)?; - let item = Self::items(&asset, &foreign_asset.fun).unwrap_or(Self::get_next_item_of(&asset)?); + let item = Self::items(&asset, asset_instance).unwrap_or(Self::get_next_item_of(&asset)?); Ok((asset, item)) } } diff --git a/xnft/src/lib.rs b/xnft/src/lib.rs index b9d0c4e14..e43820ad1 100644 --- a/xnft/src/lib.rs +++ b/xnft/src/lib.rs @@ -12,7 +12,7 @@ use frame_system::Config as SystemConfig; use scale_info::TypeInfo; use sp_runtime::{traits::AccountIdConversion, DispatchError, DispatchResult, RuntimeDebug}; use sp_std::{boxed::Box, vec::Vec}; -use xcm::v3::{AssetId, MultiAsset}; +use xcm::v3::{AssetId, AssetInstance, MultiAsset}; pub mod impl_matches; pub mod impl_nonfungibles; @@ -69,7 +69,7 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn items)] pub type ItemsMapping = - StorageDoubleMap<_, Twox64Concat, CollectionIdOf, Twox64Concat, Fungibility, ItemIdOf, OptionQuery>; + StorageDoubleMap<_, Twox64Concat, CollectionIdOf, Twox64Concat, AssetInstance, ItemIdOf, OptionQuery>; #[pallet::storage] pub type NextItemId = StorageMap<_, Twox64Concat, CollectionIdOf, ItemIdOf, OptionQuery>;