diff --git a/pallets/nft-migration/src/lib.rs b/pallets/nft-migration/src/lib.rs index bda09908..af36cd54 100644 --- a/pallets/nft-migration/src/lib.rs +++ b/pallets/nft-migration/src/lib.rs @@ -27,7 +27,7 @@ use sp_runtime::{offchain::http::Request, DispatchResult}; use sp_std::prelude::*; use core_primitives::{ - CollectionType, NFTTrait, NftAssetData, NftClassData, NftGroupCollectionData, NftMetadata, TokenType, + CollectionType, NFTMigrationTrait, NftAssetData, NftClassData, NftGroupCollectionData, NftMetadata, TokenType, }; use primitives::{Attributes, ClassId, GroupCollectionId, TokenId}; @@ -58,7 +58,7 @@ pub mod pallet { /// Currency type type Currency: Currency + ReservableCurrency; /// NFT trait required for minting NFTs - type NFTSource: NFTTrait, ClassId = ClassId, TokenId = TokenId>; + type NFTSource: NFTMigrationTrait, ClassId = ClassId, TokenId = TokenId>; /// Accounts that can set start migration type MigrationOrigin: EnsureOrigin; /// Extrinsics' weights @@ -72,6 +72,8 @@ pub mod pallet { MigrationInProgress, /// No Pioneer data is found at given endpoint PioneerDataNotFound, + /// Provided migration data is inconsistent with the current state of the chain + InconsistentMigrationData, } #[pallet::event] @@ -132,58 +134,47 @@ pub mod pallet { collection_data: NftGroupCollectionData, ) -> DispatchResult { ensure_none(origin)?; - // TODO: Validate that the collection_id parameter is the next available collection_id - // TODO: Expose collection creation to NFTTrait and mint a collection - + ensure!( + T::NFTSource::get_next_collection_id() == collection_id, + Error::::InconsistentMigrationData + ); + T::NFTSource::migrate_collection(collection_id, collection_data); Ok(()) } #[pallet::weight(::WeightInfo::start_migration())] pub fn migrate_class_unsigned( origin: OriginFor, + owner: T::AccountId, collection_id: GroupCollectionId, class_id: ClassId, metadata: NftMetadata, class_data: NftClassData>, ) -> DispatchResult { ensure_none(origin)?; - // TODO: Validate that the class_id parameter is the next available class_id - /* - T::NFTSource::create_token_class( - // TODO: Use MigrationOrigin to create class with specified owner class owner - T::MigrationOrigin::get(), - metadata, - class_data.attributes, - collection_id, - class_data.token_type, - class_data.collection_type, - class_data.royalty_fee, - class_data.mint_limit, - )?; - */ + ensure!( + T::NFTSource::get_next_class_id() == class_id, + Error::::InconsistentMigrationData + ); + T::NFTSource::migrate_class(&owner, class_id, collection_id, metadata, class_data)?; Ok(()) } #[pallet::weight(::WeightInfo::start_migration())] pub fn migrate_token_unsigned( origin: OriginFor, + owner: T::AccountId, class_id: ClassId, token_id: TokenId, metadata: NftMetadata, token_data: NftAssetData>, ) -> DispatchResult { ensure_none(origin)?; - // TODO: Validate that the token_id parameter is the next available token_id - // TODO: Use MigrationOrigin to create token with specified owner token owner - /* - T::NFTSource::mint_token_with_id( - T::MigrationOrigin::get(), - class_id, - token_id, - metadata, - token_data.attributes, - )?; - */ + ensure!( + T::NFTSource::get_next_token_id(class_id) == token_id, + Error::::InconsistentMigrationData + ); + T::NFTSource::migrate_token(&owner, token_id, class_id, metadata, token_data)?; Ok(()) } } @@ -196,11 +187,16 @@ impl Pallet { Self::fetch_pioneer_nft_collections_data(PIONEER_COLLECTIONS_HTTP_ENDPOINT)?; Self::create_nft_collections_from_pioneer_data(&pioneer_collections_data)?; - let pioneer_class_data: Vec<(GroupCollectionId, ClassId, NftMetadata, NftClassData>)> = - Self::fetch_pioneer_nft_class_data(PIONEER_CLASSES_HTTP_ENDPOINT)?; + let pioneer_class_data: Vec<( + T::AccountId, + GroupCollectionId, + ClassId, + NftMetadata, + NftClassData>, + )> = Self::fetch_pioneer_nft_class_data(PIONEER_CLASSES_HTTP_ENDPOINT)?; Self::create_nft_classes_from_pioneer_data(&pioneer_class_data)?; - let pioneer_token_data: Vec<(ClassId, TokenId, NftMetadata, NftAssetData>)> = + let pioneer_token_data: Vec<(T::AccountId, ClassId, TokenId, NftMetadata, NftAssetData>)> = Self::fetch_pioneer_nft_token_data(PIONEER_TOKENS_HTTP_ENDPOINT)?; Self::mint_nft_tokens_from_pioneer_data(&pioneer_token_data)?; @@ -227,6 +223,44 @@ impl Pallet { return Ok(vec![]); } + /// Fetches Pioneer classes data from database via HTTP + fn fetch_pioneer_nft_class_data( + endpoint_address: &str, + ) -> Result< + Vec<( + T::AccountId, + GroupCollectionId, + ClassId, + NftMetadata, + NftClassData>, + )>, + DispatchError, + > { + let pioneer_classes_request = Request::get(endpoint_address); + // TODO: Add correct request header + let pending = pioneer_classes_request.add_header("X-Auth", "hunter2").send().unwrap(); + 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); + return Ok(vec![]); + } + /// Fetches Pioneer tokens data from database via HTTP + fn fetch_pioneer_nft_token_data( + endpoint_address: &str, + ) -> Result>)>, DispatchError> { + let pioneer_tokens_request = Request::get(endpoint_address); + // TODO: Add correct request header + let pending = pioneer_tokens_request.add_header("X-Auth", "hunter2").send().unwrap(); + 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); + return Ok(vec![]); + } + fn create_nft_collections_from_pioneer_data( pioneer_collections_data: &Vec<(GroupCollectionId, NftGroupCollectionData)>, ) -> DispatchResult { @@ -236,72 +270,50 @@ impl Pallet { collection_data: (*collection_data).clone(), }; SubmitTransaction::>::submit_unsigned_transaction(call.into()) - .map_err(|()| "Unable to submit unsigned transaction.")?; + .map_err(|()| "Unable to submit unsigned collection migration transaction.")?; } //Self::deposit_event(Event::::CollectionDataMigrationCompleted); Ok(()) } - /// Fetches Pioneer classes data from database via HTTP - fn fetch_pioneer_nft_class_data( - endpoint_address: &str, - ) -> Result>)>, DispatchError> { - let pioneer_classes_request = Request::get(endpoint_address); - // TODO: Add correct request header - let pending = pioneer_classes_request.add_header("X-Auth", "hunter2").send().unwrap(); - let mut response = pending.wait().unwrap(); - let body = response.body(); - ensure!(!body.error().is_none(), Error::::PioneerDataNotFound); - // TODO: Process data into Vec<(GroupCollectionId, ClassId, NftMetadata, - // NftClassData>)> Self::deposit_event(Event::::FetchedClassData); - return Ok(vec![]); - } - fn create_nft_classes_from_pioneer_data( - pioneer_class_data: &Vec<(GroupCollectionId, ClassId, NftMetadata, NftClassData>)>, + pioneer_class_data: &Vec<( + T::AccountId, + GroupCollectionId, + ClassId, + NftMetadata, + NftClassData>, + )>, ) -> DispatchResult { - for (collection_id, class_id, metadata, class_data) in pioneer_class_data.iter() { + for (owner, collection_id, class_id, metadata, class_data) in pioneer_class_data.iter() { let call = Call::migrate_class_unsigned { + owner: (*owner).clone(), collection_id: *collection_id, class_id: *class_id, metadata: (*metadata).clone(), class_data: (*class_data).clone(), }; SubmitTransaction::>::submit_unsigned_transaction(call.into()) - .map_err(|()| "Unable to submit unsigned transaction.")?; + .map_err(|()| "Unable to submit unsigned class migration transaction.")?; } //Self::deposit_event(Event::::ClassDataMigrationCompleted); Ok(()) } - /// Fetches Pioneer tokens data from database via HTTP - fn fetch_pioneer_nft_token_data( - endpoint_address: &str, - ) -> Result>)>, DispatchError> { - let pioneer_tokens_request = Request::get(endpoint_address); - // TODO: Add correct request header - let pending = pioneer_tokens_request.add_header("X-Auth", "hunter2").send().unwrap(); - let mut response = pending.wait().unwrap(); - let body = response.body(); - ensure!(!body.error().is_none(), Error::::PioneerDataNotFound); - // TODO: Process data into Vec<(ClassId, TokenId, NftMetadata, NftAssetData>)> - //Self::deposit_event(Event::::FetchedTokenData); - return Ok(vec![]); - } - + /// Internally migrate Pioneer tokens using the fetched data fn mint_nft_tokens_from_pioneer_data( - pioneer_token_data: &Vec<(ClassId, TokenId, NftMetadata, NftAssetData>)>, + pioneer_token_data: &Vec<(T::AccountId, ClassId, TokenId, NftMetadata, NftAssetData>)>, ) -> DispatchResult { - for (class_id, token_id, metadata, token_data) in pioneer_token_data.iter() { - // TODO: Mint new tokens + for (owner, class_id, token_id, metadata, token_data) in pioneer_token_data.iter() { let call = Call::migrate_token_unsigned { + owner: (*owner).clone(), class_id: *class_id, token_id: *token_id, metadata: (*metadata).clone(), token_data: (*token_data).clone(), }; SubmitTransaction::>::submit_unsigned_transaction(call.into()) - .map_err(|()| "Unable to submit unsigned transaction.")?; + .map_err(|()| "Unable to submit unsigned token migration transaction.")?; } //Self::deposit_event(Event::::TokenDataMigrationCompleted); Ok(()) diff --git a/pallets/nft/src/lib.rs b/pallets/nft/src/lib.rs index 24f3f244..ff0897d2 100644 --- a/pallets/nft/src/lib.rs +++ b/pallets/nft/src/lib.rs @@ -49,7 +49,9 @@ use sp_std::vec::Vec; use auction_manager::{Auction, CheckAuctionItemHandler}; pub use pallet::*; -pub use primitive_traits::{Attributes, NFTTrait, NftClassData, NftGroupCollectionData, NftMetadata, TokenType}; +pub use primitive_traits::{ + Attributes, NFTMigrationTrait, NFTTrait, NftClassData, NftGroupCollectionData, NftMetadata, TokenType, +}; use primitive_traits::{CollectionType, NftAssetData, NftClassDataV1, PreSignedMint}; use primitives::{AssetId, ClassId, GroupCollectionId, ItemId, TokenId}; pub use weights::WeightInfo; @@ -1767,3 +1769,47 @@ impl NFTTrait> for Pallet { Ok(()) } } + +impl NFTMigrationTrait> for Pallet { + type TokenId = TokenIdOf; + type ClassId = ClassIdOf; + + fn get_next_collection_id() -> GroupCollectionId { + Self::next_group_collection_id() + } + + fn get_next_class_id() -> Self::ClassId { + NftModule::::next_class_id() + } + + fn get_next_token_id(class_id: Self::ClassId) -> Self::TokenId { + NftModule::::next_token_id(class_id) + } + + fn migrate_collection( + colllection_id: GroupCollectionId, + collection_data: NftGroupCollectionData, + ) -> DispatchResult { + Ok(()) + } + + fn migrate_class( + owner: &T::AccountId, + class_id: Self::ClassId, + collection_id: GroupCollectionId, + class_metadata: NftMetadata, + class_data: NftClassData>, + ) -> sp_runtime::DispatchResult { + Ok(()) + } + + fn migrate_token( + owner: &T::AccountId, + token_id: Self::TokenId, + class_id: Self::ClassId, + token_metadata: NftMetadata, + token_data: NftAssetData>, + ) -> sp_runtime::DispatchResult { + Ok(()) + } +} diff --git a/runtime/continuum/src/lib.rs b/runtime/continuum/src/lib.rs index 82b4ef79..f9131e48 100644 --- a/runtime/continuum/src/lib.rs +++ b/runtime/continuum/src/lib.rs @@ -58,7 +58,12 @@ use sp_core::{crypto::KeyTypeId, ConstBool, OpaqueMetadata}; use sp_runtime::traits::{AccountIdConversion, ConstU32, Convert, ConvertInto, Extrinsic as ExtrinsicT}; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; -use sp_runtime::{create_runtime_str, generic, impl_opaque_keys, traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, IdentifyAccount, Verify}, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, MultiSignature, RuntimeDebug, SaturatedConversion}; +use sp_runtime::{ + create_runtime_str, generic, impl_opaque_keys, + traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, IdentifyAccount, Verify}, + transaction_validity::{TransactionSource, TransactionValidity}, + ApplyExtrinsicResult, MultiSignature, RuntimeDebug, SaturatedConversion, +}; pub use sp_runtime::{MultiAddress, Perbill, Percent, Permill}; use sp_std::prelude::*; #[cfg(feature = "std")] @@ -1812,8 +1817,10 @@ where ) -> Option<(RuntimeCall, ::SignaturePayload)> { use sp_runtime::traits::StaticLookup; // take the biggest period possible. - let period = - BlockHashCount::get().checked_next_power_of_two().map(|c| c / 2).unwrap_or(2) as u64; + let period = BlockHashCount::get() + .checked_next_power_of_two() + .map(|c| c / 2) + .unwrap_or(2) as u64; let current_block = System::block_number() .saturated_into::() @@ -1826,10 +1833,7 @@ where frame_system::CheckSpecVersion::::new(), frame_system::CheckTxVersion::::new(), frame_system::CheckGenesis::::new(), - frame_system::CheckMortality::::from(generic::Era::mortal( - period, - current_block, - )), + frame_system::CheckMortality::::from(generic::Era::mortal(period, current_block)), frame_system::CheckNonce::::from(nonce), frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(tip), @@ -1948,8 +1952,6 @@ construct_runtime!( } ); - - impl_runtime_apis! { impl sp_consensus_aura::AuraApi for Runtime { fn slot_duration() -> sp_consensus_aura::SlotDuration { diff --git a/runtime/metaverse/src/lib.rs b/runtime/metaverse/src/lib.rs index f7dee4d4..375b0a0b 100644 --- a/runtime/metaverse/src/lib.rs +++ b/runtime/metaverse/src/lib.rs @@ -1532,8 +1532,10 @@ where ) -> Option<(RuntimeCall, ::SignaturePayload)> { use sp_runtime::traits::StaticLookup; // take the biggest period possible. - let period = - BlockHashCount::get().checked_next_power_of_two().map(|c| c / 2).unwrap_or(2) as u64; + let period = BlockHashCount::get() + .checked_next_power_of_two() + .map(|c| c / 2) + .unwrap_or(2) as u64; let current_block = System::block_number() .saturated_into::() @@ -1546,10 +1548,7 @@ where frame_system::CheckSpecVersion::::new(), frame_system::CheckTxVersion::::new(), frame_system::CheckGenesis::::new(), - frame_system::CheckMortality::::from(generic::Era::mortal( - period, - current_block, - )), + frame_system::CheckMortality::::from(generic::Era::mortal(period, current_block)), frame_system::CheckNonce::::from(nonce), frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(tip), @@ -1663,7 +1662,6 @@ construct_runtime!( } ); - pub struct TransactionConverter; impl fp_rpc::ConvertTransaction for TransactionConverter { diff --git a/traits/core-primitives/src/lib.rs b/traits/core-primitives/src/lib.rs index 547ff88f..20b927c5 100644 --- a/traits/core-primitives/src/lib.rs +++ b/traits/core-primitives/src/lib.rs @@ -328,6 +328,38 @@ pub trait NFTTrait { ) -> DispatchResult; } +pub trait NFTMigrationTrait { + /// Token identifier + type TokenId; + /// Token class identifier + type ClassId; + /// Get the next collection id + fn get_next_collection_id() -> GroupCollectionId; + /// Get the next class id + fn get_next_class_id() -> Self::ClassId; + /// Get the current token id + fn get_next_token_id(class_id: Self::ClassId) -> Self::TokenId; + /// Migrate collection + fn migrate_collection(colllection_id: GroupCollectionId, collection_data: NftGroupCollectionData) + -> DispatchResult; + /// Migrate class + fn migrate_class( + owner: &AccountId, + class_id: Self::ClassId, + collection_id: GroupCollectionId, + class_metadata: NftMetadata, + class_data: NftClassData, + ) -> DispatchResult; + /// Migrate token + fn migrate_token( + owner: &AccountId, + token_id: Self::TokenId, + class_id: Self::ClassId, + token_metadata: NftMetadata, + token_data: NftAssetData, + ) -> DispatchResult; +} + pub trait RoundTrait { fn get_current_round_info() -> RoundInfo; }