--- a/pallets/nonfungible/Cargo.toml +++ b/pallets/nonfungible/Cargo.toml @@ -18,6 +18,7 @@ sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.20" } pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.20" } pallet-common = { default-features = false, path = '../common' } +pallet-structure = { default-features = false, path = '../structure' } up-data-structs = { default-features = false, path = '../../primitives/data-structs' } evm-coder = { default-features = false, path = '../../crates/evm-coder' } pallet-evm-coder-substrate = { default-features = false, path = '../evm-coder-substrate' } @@ -36,6 +37,7 @@ "sp-std/std", "up-data-structs/std", "pallet-common/std", + "pallet-structure/std", "evm-coder/std", "ethereum/std", "pallet-evm-coder-substrate/std", --- a/pallets/nonfungible/src/lib.rs +++ b/pallets/nonfungible/src/lib.rs @@ -27,6 +27,7 @@ Error as CommonError, Pallet as PalletCommon, Event as CommonEvent, CollectionHandle, dispatch::CollectionDispatch, }; +use pallet_structure::Pallet as PalletStructure; use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder}; use sp_core::H160; use sp_runtime::{ArithmeticError, DispatchError, DispatchResult}; @@ -69,7 +70,9 @@ } #[pallet::config] - pub trait Config: frame_system::Config + pallet_common::Config { + pub trait Config: + frame_system::Config + pallet_common::Config + pallet_structure::Config + { type WeightInfo: WeightInfo; } @@ -256,6 +259,7 @@ let token_data = >::get((collection.id, token)).ok_or(>::TokenNotFound)?; + // TODO: require sender to be token, owner, require admins to go through transfer_from ensure!( &token_data.owner == from || (collection.limits.owner_can_transfer() && collection.is_owner_or_admin(from)), @@ -502,33 +506,50 @@ Ok(()) } - pub fn transfer_from( + fn check_allowed( collection: &NonfungibleHandle, spender: &T::CrossAccountId, from: &T::CrossAccountId, - to: &T::CrossAccountId, token: TokenId, ) -> DispatchResult { if spender.conv_eq(from) { - return Self::transfer(collection, from, to, token); + return Ok(()); } if collection.access == AccessMode::AllowList { // `from`, `to` checked in [`transfer`] collection.check_allowlist(spender)?; } - - if >::get((collection.id, token)).as_ref() != Some(spender) { + if let Some(source) = T::CrossTokenAddressMapping::address_to_token(from) { + // TODO: should collection owner be allowed to perform this transfer? ensure!( - collection.ignores_allowance(spender), - >::ApprovedValueTooLow + >::indirectly_owned(spender.clone(), source.0, source.1, 1)?, + >::ApprovedValueTooLow, ); + return Ok(()); + } + if >::get((collection.id, token)).as_ref() == Some(spender) { + return Ok(()); } + ensure!( + collection.ignores_allowance(spender), + >::ApprovedValueTooLow + ); + Ok(()) + } + + pub fn transfer_from( + collection: &NonfungibleHandle, + spender: &T::CrossAccountId, + from: &T::CrossAccountId, + to: &T::CrossAccountId, + token: TokenId, + ) -> DispatchResult { + Self::check_allowed(collection, spender, from, token)?; // ========= - Self::transfer(collection, from, to, token)?; // Allowance is reset in [`transfer`] - Ok(()) + Self::transfer(collection, from, to, token) } pub fn burn_from( @@ -537,20 +558,7 @@ from: &T::CrossAccountId, token: TokenId, ) -> DispatchResult { - if spender.conv_eq(from) { - return Self::burn(collection, from, token); - } - if collection.access == AccessMode::AllowList { - // `from` checked in [`burn`] - collection.check_allowlist(spender)?; - } - - if >::get((collection.id, token)).as_ref() != Some(spender) { - ensure!( - collection.ignores_allowance(spender), - >::ApprovedValueTooLow - ); - } + Self::check_allowed(collection, spender, from, token)?; // =========