From ab9f8fba24d25c9200e2837487587a890561d236 Mon Sep 17 00:00:00 2001 From: chexware Date: Thu, 18 Jul 2024 18:26:00 +0100 Subject: [PATCH] feat: initial implementation of new NFTMigrationTrait trait and finalised creation of collections, classes and tokens for for migration pallet --- Cargo.lock | 7 + pallets/nft-migration/Cargo.toml | 12 +- pallets/nft-migration/src/lib.rs | 10 +- pallets/nft-migration/src/mock.rs | 356 +++++++++++++++++++---------- pallets/nft-migration/src/tests.rs | 6 +- pallets/nft/src/lib.rs | 59 +++-- 6 files changed, 300 insertions(+), 150 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7c94bbdf..40e46f44 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8042,12 +8042,19 @@ dependencies = [ name = "pallet-nft-migration" version = "2.0.0-rc6" dependencies = [ + "auction-manager", "bit-country-primitives", "core-primitives", + "currencies", "frame-benchmarking", "frame-support", "frame-system", + "orml-nft", + "orml-tokens", + "orml-traits", "pallet-balances", + "pallet-nft", + "pallet-proxy", "parity-scale-codec", "scale-info", "smallvec", diff --git a/pallets/nft-migration/Cargo.toml b/pallets/nft-migration/Cargo.toml index b3cf666d..e7609734 100644 --- a/pallets/nft-migration/Cargo.toml +++ b/pallets/nft-migration/Cargo.toml @@ -21,14 +21,22 @@ frame-support = { workspace = true } frame-system = { workspace = true } frame-benchmarking = { workspace = true, optional = true } +pallet-balances = { workspace = true } + core-primitives = { path = "../../traits/core-primitives", default-features = false } primitives = { package = "bit-country-primitives", path = "../../primitives/metaverse", default-features = false } -pallet-balances = { workspace = true } [dev-dependencies] sp-core = { workspace = true } sp-io = { workspace = true } smallvec = { workspace = true } +orml-tokens = { workspace = true, default-features = true } +orml-nft = { workspace = true, default-features = true } +orml-traits = { workspace = true, default-features = true } +pallet-nft = { path = "../nft" } +currencies = { path = "../currencies" } +pallet-proxy = { workspace = true, default-features = true } +auction-manager = { package = "auction-manager", path = "../../traits/auction-manager" } [features] runtime-benchmarks = [ @@ -45,5 +53,5 @@ std = [ "frame-support/std", "frame-system/std", "frame-benchmarking/std", - "pallet-balances/std" + "pallet-balances/std", ] \ No newline at end of file diff --git a/pallets/nft-migration/src/lib.rs b/pallets/nft-migration/src/lib.rs index af36cd54..080c3b71 100644 --- a/pallets/nft-migration/src/lib.rs +++ b/pallets/nft-migration/src/lib.rs @@ -242,8 +242,9 @@ impl Pallet { let mut response = pending.wait().unwrap(); let body = response.body(); ensure!(!body.error().is_none(), Error::::PioneerDataNotFound); - // TODO: Process data into Vec<(T::AccountId, GroupCollectionId, ClassId, NftMetadata, NftClassData>)> - // NftClassData>)> Self::deposit_event(Event::::FetchedClassData); + // TODO: Process data into Vec<(T::AccountId, GroupCollectionId, ClassId, NftMetadata, + // NftClassData>)> NftClassData>)> + // Self::deposit_event(Event::::FetchedClassData); return Ok(vec![]); } /// Fetches Pioneer tokens data from database via HTTP @@ -256,8 +257,9 @@ impl Pallet { let mut response = pending.wait().unwrap(); let body = response.body(); ensure!(!body.error().is_none(), Error::::PioneerDataNotFound); - // TODO: Process data into Vec<(T::AccountId, ClassId, TokenId, NftMetadata, NftAssetData>)> - // NftAssetData>)> Self::deposit_event(Event::::FetchedTokenData); + // TODO: Process data into Vec<(T::AccountId, ClassId, TokenId, NftMetadata, + // NftAssetData>)> NftAssetData>)> + // Self::deposit_event(Event::::FetchedTokenData); return Ok(vec![]); } diff --git a/pallets/nft-migration/src/mock.rs b/pallets/nft-migration/src/mock.rs index 8c631e1b..c8f3bdcd 100644 --- a/pallets/nft-migration/src/mock.rs +++ b/pallets/nft-migration/src/mock.rs @@ -1,25 +1,40 @@ #![cfg(test)] +use auction_manager::{Auction, AuctionInfo, AuctionItem, AuctionType, CheckAuctionItemHandler, ListingLevel}; use core_primitives::{CollectionType, NftClassData, TokenType}; -use frame_support::{construct_runtime, ord_parameter_types, parameter_types, PalletId}; +use frame_support::{ + construct_runtime, ord_parameter_types, parameter_types, + traits::{ConstU128, InstanceFilter, Nothing}, + PalletId, +}; use frame_system::EnsureSignedBy; -use primitives::{Attributes, ClassId, FungibleTokenId, GroupCollectionId, NftMetadata, TokenId}; -use sp_runtime::testing::H256; +use orml_traits::parameter_type_with_key; +use primitives::{ + Amount, Attributes, AuctionId, ClassId, FungibleTokenId, GroupCollectionId, ItemId, NftMetadata, TokenId, +}; +use sp_core::crypto::AccountId32; +use sp_runtime::traits::{BlakeTwo256, IdentifyAccount}; use sp_runtime::{ - traits::{ConvertInto, IdentityLookup}, - BuildStorage, DispatchError, Perbill, + testing::{TestXt, H256}, + traits::{ConvertInto, Extrinsic as ExtrinsicT, IdentityLookup, Verify}, + BuildStorage, DispatchError, MultiSignature, Perbill, }; -use sp_std::collections::btree_map::BTreeMap; -use sp_std::default::Default; +use sp_std::{collections::btree_map::BTreeMap, default::Default}; use super::*; use crate as nft_migration; -pub type AccountId = u128; +//pub type AccountId = u128; pub type Balance = u128; pub type BlockNumber = u64; -pub const ALICE: AccountId = 1; -pub const BOB: AccountId = 5; + +pub type AccountId = ::AccountId; +pub type Signature = MultiSignature; +pub type AccountPublic = ::Signer; +pub type Extrinsic = TestXt; + +pub const ALICE: AccountId = AccountId32::new([1; 32]); +pub const BOB: AccountId = AccountId32::new([5; 32]); fn test_attributes(x: u8) -> Attributes { let mut attr: Attributes = BTreeMap::new(); @@ -84,160 +99,253 @@ impl pallet_balances::Config for Runtime { type MaxFreezes = frame_support::traits::ConstU32<0>; } -pub struct MockNFTHandler; +parameter_type_with_key! { + pub ExistentialDeposits: |_currency_id: FungibleTokenId| -> Balance { + Default::default() + }; +} + +impl orml_tokens::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type Amount = Amount; + type CurrencyId = FungibleTokenId; + type WeightInfo = (); + type ExistentialDeposits = ExistentialDeposits; + type CurrencyHooks = (); + type MaxLocks = (); + type ReserveIdentifier = [u8; 8]; + type MaxReserves = (); + type DustRemovalWhitelist = Nothing; +} -impl NFTTrait for MockNFTHandler { - type TokenId = TokenId; - type ClassId = ClassId; +pub type AdaptedBasicCurrency = currencies::BasicCurrencyAdapter; - fn check_ownership(who: &AccountId, asset_id: &(Self::ClassId, Self::TokenId)) -> Result { - Ok(false) - } +parameter_types! { + pub const NativeCurrencyId: FungibleTokenId = FungibleTokenId::NativeToken(0); +} - fn check_collection_and_class( - collection_id: GroupCollectionId, - class_id: Self::ClassId, - ) -> Result { - Ok(false) +impl currencies::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type MultiSocialCurrency = Tokens; + type NativeCurrency = AdaptedBasicCurrency; + type GetNativeCurrencyId = NativeCurrencyId; + type WeightInfo = (); +} + +parameter_types! { + pub MaxClassMetadata: u32 = 1024; + pub MaxTokenMetadata: u32 = 1024; +} + +impl orml_nft::Config for Runtime { + type ClassId = u32; + type TokenId = u64; + type Currency = Balances; + type ClassData = NftClassData; + type TokenData = NftAssetData; + type MaxClassMetadata = MaxClassMetadata; + type MaxTokenMetadata = MaxTokenMetadata; +} + +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, RuntimeDebug, MaxEncodedLen, TypeInfo)] +pub enum ProxyType { + Any, + JustTransfer, +} +impl Default for ProxyType { + fn default() -> Self { + Self::Any } - fn get_nft_group_collection(_nft_collection: &Self::ClassId) -> Result { - Ok(0) +} +impl InstanceFilter for ProxyType { + fn filter(&self, c: &RuntimeCall) -> bool { + match self { + ProxyType::Any => true, + ProxyType::JustTransfer => matches!(c, RuntimeCall::Balances(pallet_balances::Call::transfer { .. })), + } } - - fn is_stackable(_asset_id: (Self::ClassId, Self::TokenId)) -> Result { - Ok(false) + fn is_superset(&self, o: &Self) -> bool { + self == &ProxyType::Any || self == o } +} - fn create_token_class( - sender: &AccountId, - _metadata: NftMetadata, - _attributes: Attributes, - collection_id: GroupCollectionId, - _token_type: TokenType, - _collection_type: CollectionType, - _royalty_fee: Perbill, - _mint_limit: Option, - ) -> Result { - Ok(0) - } +impl pallet_proxy::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type ProxyType = ProxyType; + type ProxyDepositBase = ConstU128<1>; + type ProxyDepositFactor = ConstU128<1>; + type MaxProxies = ConstU32<4>; + type WeightInfo = (); + type CallHasher = BlakeTwo256; + type MaxPending = ConstU32<2>; + type AnnouncementDepositBase = ConstU128<1>; + type AnnouncementDepositFactor = ConstU128<1>; +} - fn mint_token( - sender: &AccountId, - class_id: ClassId, - _metadata: NftMetadata, - _attributes: Attributes, - ) -> Result { - Ok(1) - } +pub struct MockAuctionManager; - fn transfer_nft(_from: &AccountId, _to: &AccountId, _nft: &(Self::ClassId, Self::TokenId)) -> DispatchResult { - Ok(()) +impl Auction for MockAuctionManager { + type Balance = Balance; + + fn auction_info(_id: u64) -> Option> { + None } - fn check_item_on_listing(_class_id: Self::ClassId, _token_id: Self::TokenId) -> Result { - Ok(true) + fn auction_item(_id: AuctionId) -> Option> { + None } - fn burn_nft(_account: &AccountId, _nft: &(Self::ClassId, Self::TokenId)) -> DispatchResult { + fn update_auction( + _id: u64, + _info: AuctionInfo, + ) -> frame_support::dispatch::DispatchResult { Ok(()) } - fn is_transferable(_nft: &(Self::ClassId, Self::TokenId)) -> Result { - Ok(true) + + fn update_auction_item(_id: AuctionId, _item_id: ItemId) -> frame_support::dispatch::DispatchResult { + Ok(()) } - fn get_class_fund(_class_id: &Self::ClassId) -> AccountId { - 0 + fn new_auction( + _recipient: AccountId32, + _initial_amount: Self::Balance, + _start: u64, + _end: Option, + ) -> Result { + Ok(0) } - fn get_nft_detail(_asset_id: (Self::ClassId, Self::TokenId)) -> Result, DispatchError> { - let new_data = NftClassData { - deposit: 0, - attributes: test_attributes(1), - token_type: TokenType::Transferable, - collection_type: CollectionType::Collectable, - is_locked: false, - royalty_fee: Perbill::from_percent(0u32), - mint_limit: None, - total_minted_tokens: 0u32, - }; - Ok(new_data) + fn create_auction( + _auction_type: AuctionType, + _item_id: ItemId, + _end: Option, + _recipient: AccountId32, + _initial_amount: Self::Balance, + _start: u64, + _listing_level: ListingLevel, + _listing_fee: Perbill, + _currency_id: FungibleTokenId, + ) -> Result { + Ok(0) } - fn set_lock_collection(_class_id: Self::ClassId, _is_locked: bool) -> sp_runtime::DispatchResult { + fn remove_auction(_id: u64, _item_id: ItemId) {} + + fn auction_bid_handler( + _from: AccountId, + _id: AuctionId, + _value: Self::Balance, + ) -> frame_support::dispatch::DispatchResult { Ok(()) } - fn set_lock_nft(_token_id: (Self::ClassId, Self::TokenId), _is_locked: bool) -> sp_runtime::DispatchResult { + fn buy_now_handler( + _from: AccountId, + _auction_id: AuctionId, + _value: Self::Balance, + ) -> frame_support::dispatch::DispatchResult { Ok(()) } - fn get_nft_class_detail(_class_id: Self::ClassId) -> Result, DispatchError> { - let new_data = NftClassData { - deposit: 0, - attributes: test_attributes(1), - token_type: TokenType::Transferable, - collection_type: CollectionType::Collectable, - is_locked: false, - royalty_fee: Perbill::from_percent(0u32), - mint_limit: None, - total_minted_tokens: 0u32, - }; - Ok(new_data) + fn local_auction_bid_handler( + _: BlockNumber, + _: u64, + _: ( + AccountId, + >::Balance, + ), + _: std::option::Option<( + AccountId, + >::Balance, + )>, + _: FungibleTokenId, + ) -> Result<(), sp_runtime::DispatchError> { + Ok(()) } - fn get_total_issuance(_class_id: Self::ClassId) -> Result { - Ok(1u64) + fn collect_royalty_fee( + _high_bid_price: &Self::Balance, + _high_bidder: &AccountId32, + _asset_id: &(u32, u64), + _social_currency_id: FungibleTokenId, + ) -> frame_support::dispatch::DispatchResult { + Ok(()) } +} - fn get_asset_owner(_asset_id: &(Self::ClassId, Self::TokenId)) -> Result { - Ok(ALICE) +impl CheckAuctionItemHandler for MockAuctionManager { + fn check_item_in_auction(_item_id: ItemId) -> bool { + return false; } +} - fn mint_token_with_id( - sender: &AccountId, - class_id: Self::ClassId, - _token_id: Self::TokenId, - _metadata: core_primitives::NftMetadata, - _attributes: core_primitives::Attributes, - ) -> Result { - Ok(2) - } +parameter_types! { + pub const AssetMintingFee: Balance = 2; + pub const ClassMintingFee: Balance = 5; + pub const StorageDepositFee: Balance = 1; + pub MaxBatchTransfer: u32 = 3; + pub MaxBatchMinting: u32 = 12; + pub MaxMetadata: u32 = 10; + pub const MiningCurrencyId: FungibleTokenId = FungibleTokenId::MiningResource(0); + pub const MetaverseNetworkTreasuryPalletId: PalletId = PalletId(*b"bit/trsy"); + pub NftPalletId: PalletId = PalletId(*b"bit/bNFT"); +} - fn get_free_stackable_nft_balance(_who: &AccountId, _asset_id: &(Self::ClassId, Self::TokenId)) -> Balance { - 0u128 - } +impl pallet_nft::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type AssetMintingFee = AssetMintingFee; + type ClassMintingFee = ClassMintingFee; + type Treasury = MetaverseNetworkTreasuryPalletId; + type Currency = Balances; + type PalletId = NftPalletId; + type AuctionHandler = MockAuctionManager; + type MaxBatchTransfer = MaxBatchTransfer; + type MaxBatchMinting = MaxBatchMinting; + type MaxMetadata = MaxMetadata; + type MultiCurrency = Currencies; + type MiningResourceId = MiningCurrencyId; + type StorageDepositFee = StorageDepositFee; + type OffchainSignature = Signature; + type OffchainPublic = AccountPublic; + type WeightInfo = (); +} - fn reserve_stackable_nft_balance( - _who: &AccountId, - _asset_id: &(Self::ClassId, Self::TokenId), - _amount: Balance, - ) -> DispatchResult { - Ok(()) - } +impl frame_system::offchain::SigningTypes for Runtime { + type Public = ::Signer; + type Signature = Signature; +} - fn unreserve_stackable_nft_balance( - _who: &AccountId, - _asset_id: &(Self::ClassId, Self::TokenId), - _amount: Balance, - ) -> sp_runtime::DispatchResult { - Ok(()) - } +impl frame_system::offchain::SendTransactionTypes for Runtime +where + RuntimeCall: From, +{ + type OverarchingCall = RuntimeCall; + type Extrinsic = Extrinsic; +} - fn transfer_stackable_nft( - _sender: &AccountId, - _to: &AccountId, - _nft: &(Self::ClassId, Self::TokenId), - _amount: Balance, - ) -> sp_runtime::DispatchResult { - Ok(()) +impl frame_system::offchain::CreateSignedTransaction for Runtime +where + RuntimeCall: From, +{ + fn create_transaction>( + call: RuntimeCall, + _public: ::Signer, + _account: AccountId, + nonce: u64, + ) -> Option<(RuntimeCall, ::SignaturePayload)> { + Some((call, (nonce, ()))) } } impl Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; - type NFTSource = MockNFTHandler; + type NFTSource = Nft; type MigrationOrigin = EnsureSignedBy; + type WeightInfo = (); } construct_runtime!( @@ -248,11 +356,15 @@ construct_runtime!( { System: frame_system::{Pallet, Call, Config, Storage, Event}, Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + Currencies: currencies::{ Pallet, Storage, Call, Event}, + Tokens: orml_tokens::{ Pallet, Storage, Call, Event}, + OrmlNft: orml_nft::{Pallet, Storage, Config}, + Proxy: pallet_proxy, + Nft: pallet_nft::{Pallet, Call, Storage, Event}, NftMigration: nft_migration::{Pallet, Call, Storage, Event}, } ); - -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; pub struct ExtBuilder; diff --git a/pallets/nft-migration/src/tests.rs b/pallets/nft-migration/src/tests.rs index 6def2a45..7774c66e 100644 --- a/pallets/nft-migration/src/tests.rs +++ b/pallets/nft-migration/src/tests.rs @@ -3,14 +3,14 @@ use frame_support::{assert_err, assert_noop, assert_ok}; use sp_runtime::traits::BadOrigin; -use mock::{RuntimeEvent, *}; +use mock::{RuntimeCall, RuntimeEvent, RuntimeOrigin, *}; use super::*; #[test] fn start_migration_should_not_work_from_non_admin_account() { ExtBuilder::default().build().execute_with(|| { - assert_noop!(NftMigration::start_migration(RuntimeOrigin::signed(BOB),), BadOrigin); + assert_noop!(NftMigration::start_migration(RuntimeOrigin::signed(BOB)), BadOrigin); }); } @@ -18,7 +18,7 @@ fn start_migration_should_not_work_from_non_admin_account() { fn start_migration_should_work() { ExtBuilder::default().build().execute_with(|| { assert_eq!(NftMigration::is_migration_active(), false); - assert_ok!(NftMigration::start_migration(RuntimeOrigin::signed(ALICE),)); + assert_ok!(NftMigration::start_migration(RuntimeOrigin::signed(ALICE))); assert_eq!(last_event(), RuntimeEvent::NftMigration(crate::Event::MigrationStarted)); assert_eq!(NftMigration::is_migration_active(), true); }); diff --git a/pallets/nft/src/lib.rs b/pallets/nft/src/lib.rs index ff0897d2..2a32c521 100644 --- a/pallets/nft/src/lib.rs +++ b/pallets/nft/src/lib.rs @@ -438,25 +438,7 @@ pub mod pallet { properties: NftMetadata, ) -> DispatchResultWithPostInfo { ensure_root(origin)?; - - ensure!( - name.len() as u32 <= T::MaxMetadata::get() && properties.len() as u32 <= T::MaxMetadata::get(), - Error::::ExceedMaximumMetadataLength - ); - let next_group_collection_id = Self::do_create_group_collection(name.clone(), properties.clone())?; - - let collection_data = NftGroupCollectionData { name, properties }; - - GroupCollections::::insert(next_group_collection_id, collection_data); - - let all_collection_count = Self::all_nft_collection_count(); - let new_all_nft_collection_count = all_collection_count - .checked_add(One::one()) - .ok_or("Overflow adding a new collection to total collection")?; - - AllNftGroupCollection::::set(new_all_nft_collection_count); - Self::deposit_event(Event::::NewNftCollectionCreated(next_group_collection_id)); Ok(().into()) } @@ -1053,6 +1035,10 @@ impl Pallet { /// Internal creation of group collection fn do_create_group_collection(name: Vec, properties: Vec) -> Result { + ensure!( + name.len() as u32 <= T::MaxMetadata::get() && properties.len() as u32 <= T::MaxMetadata::get(), + Error::::ExceedMaximumMetadataLength + ); let next_group_collection_id = NextGroupCollectionId::::try_mutate(|collection_id| -> Result { let current_id = *collection_id; @@ -1068,6 +1054,13 @@ impl Pallet { >::insert(next_group_collection_id, collection_data); + let all_collection_count = Self::all_nft_collection_count(); + let new_all_nft_collection_count = all_collection_count + .checked_add(One::one()) + .ok_or("Overflow adding a new collection to total collection")?; + + AllNftGroupCollection::::set(new_all_nft_collection_count); + Ok(next_group_collection_id) } @@ -1790,6 +1783,8 @@ impl NFTMigrationTrait> for Pallet { colllection_id: GroupCollectionId, collection_data: NftGroupCollectionData, ) -> DispatchResult { + Self::do_create_group_collection(collection_data.name, collection_data.properties)?; + Ok(()) } @@ -1800,7 +1795,22 @@ impl NFTMigrationTrait> for Pallet { class_metadata: NftMetadata, class_data: NftClassData>, ) -> sp_runtime::DispatchResult { - Ok(()) + Self::do_create_class( + owner, + class_metadata, + class_data.attributes, + collection_id, + class_data.token_type, + class_data.collection_type, + class_data.royalty_fee, + class_data.mint_limit, + )?; + Classes::::try_mutate(class_id, |class_info| -> DispatchResult { + let info = class_info.as_mut().ok_or(Error::::ClassIdNotFound)?; + info.data.is_locked = class_data.is_locked; + Ok(()) + }) + // TODO: Transfer class creation fees back to owner } fn migrate_token( @@ -1810,6 +1820,17 @@ impl NFTMigrationTrait> for Pallet { token_metadata: NftMetadata, token_data: NftAssetData>, ) -> sp_runtime::DispatchResult { + Self::do_mint_nft_with_token_id( + owner, + owner, + class_id, + Some(token_id), + token_metadata, + token_data.attributes, + token_data.is_locked, + false, + ); + // TODO: Transfer token minting fees back to owner Ok(()) } }