From dc1cc2f6399adf19dd7adc1cb5bc656905b11167 Mon Sep 17 00:00:00 2001 From: str-mv Date: Thu, 09 Jul 2020 11:06:37 +0000 Subject: [PATCH] NFT-56 + 47 added --- --- a/pallets/nft/src/lib.rs +++ b/pallets/nft/src/lib.rs @@ -24,6 +24,9 @@ pub struct CollectionType { pub owner: AccountId, pub next_item_id: u64, + pub name: Vec, // 64 include null escape char + pub description: Vec, // 256 include null escape char + pub token_prefix: Vec, // 16 include null escape char pub custom_data_size: u32, } @@ -58,21 +61,17 @@ /// Next available collection ID pub NextCollectionID get(fn next_collection_id): u64; - - pub Collection get(collection): map hasher(identity) u64 => CollectionType; - //pub Collection get(collection): map hasher(identity) u64 => CollectionType; - - pub AdminList get(admin_list_collection): map hasher(identity) u64 => Vec; + pub Collection get(fn collection): map hasher(identity) u64 => CollectionType; + pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec; /// Balance owner per collection map - pub Balance get(balance_count): map hasher(blake2_128_concat) (u64, T::AccountId) => u64; - pub ApprovedList get(approved): map hasher(blake2_128_concat) (u64, u64) => Vec; + pub Balance get(fn balance_count): map hasher(blake2_128_concat) (u64, T::AccountId) => u64; + pub ApprovedList get(fn approved): map hasher(blake2_128_concat) (u64, u64) => Vec; - pub ItemList get(item_id): map hasher(blake2_128_concat) (u64, u64) => NftItemType; - // pub ItemList get(item_id): map hasher(blake2_128_concat) (u64, u64) => NftItemType; + pub ItemList get(fn item_id): map hasher(blake2_128_concat) (u64, u64) => NftItemType; + pub ItemListIndex get(fn item_index): map hasher(blake2_128_concat) u64 => u64; - pub ItemListIndex get(item_index): map hasher(blake2_128_concat) u64 => u64; - // pub ItemListIndex get(item_index): map hasher(blake2_128_concat) u64 => u64; + pub AddressTokens get(fn address_tokens): map hasher(blake2_128_concat) (u64, T::AccountId) => Vec; } } @@ -82,7 +81,9 @@ where AccountId = ::AccountId, { - Created(u32, AccountId), + Created(u64, AccountId), + ItemCreated(u64), + ItemDestroyed(u64, u64), } ); @@ -109,18 +110,41 @@ // @param customDataSz size of custom data in each collection item // returns collection ID #[weight = frame_support::weights::SimpleDispatchInfo::default()] - pub fn create_collection(origin, custom_data_sz: u32) -> DispatchResult { + pub fn create_collection( origin, + collection_name: Vec, + collection_description: Vec, + token_prefix: Vec, + custom_data_sz: u32) -> DispatchResult { + // Anyone can create a collection let who = ensure_signed(origin)?; + // check params + let mut name = collection_name.to_vec(); + name.push(0); + ensure!(name.len() <= 64, "Collection name can not be longer than 63 char"); + + let mut description = collection_description.to_vec(); + description.push(0); + ensure!(name.len() <= 256, "Collection description can not be longer than 255 char"); + + let mut prefix = token_prefix.to_vec(); + prefix.push(0); + ensure!(prefix.len() <= 16, "Token prefix can not be longer than 15 char"); + // Generate next collection ID - let next_id = NextCollectionID::get(); + let next_id = NextCollectionID::get() + .checked_add(1) + .expect("collection id error"); NextCollectionID::put(next_id); // Create new collection let new_collection = CollectionType { - owner: who, + owner: who.clone(), + name: name, + description: description, + token_prefix: prefix, next_item_id: next_id, custom_data_size: custom_data_sz, }; @@ -128,6 +152,9 @@ // Add new collection to map >::insert(next_id, new_collection); + // call event + Self::deposit_event(RawEvent::Created(next_id, who.clone())); + Ok(()) } @@ -247,10 +274,19 @@ data: properties, }; - let current_index = ::get(collection_id); + + let current_index = ::get(collection_id) + .checked_add(1) + .expect("Item list index id error"); + + Self::add_token_index(collection_id, current_index, new_item.owner.clone())?; + ::insert(collection_id, current_index); >::insert((collection_id, current_index), new_item); + // call event + Self::deposit_event(RawEvent::ItemCreated(collection_id)); + Ok(()) } @@ -279,10 +315,15 @@ } >::remove((collection_id, item_id)); + Self::remove_token_index(collection_id, item_id, item.owner.clone())?; + // update balance let new_balance = >::get((collection_id, item.owner.clone())) - 1; >::insert((collection_id, item.owner.clone()), new_balance); + // call event + Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id)); + Ok(()) } @@ -319,9 +360,13 @@ >::insert((collection_id, new_owner.clone()), balance_new_owner); // change owner - item.owner = new_owner; + let old_owner = item.owner.clone(); + item.owner = new_owner.clone(); >::insert((collection_id, item_id), item); + // update index collection + Self::move_token_index(collection_id, item_id, old_owner, new_owner.clone())?; + // reset approved list let itm: Vec = Vec::new(); >::insert((collection_id, item_id), itm); @@ -401,3 +446,55 @@ } } } + + +impl Module { + fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult { + + let list_exists = >::contains_key((collection_id, owner.clone())); + if list_exists { + + let mut list = >::get((collection_id, owner.clone())); + let item_contains = list.contains(&item_index.clone()); + + if !item_contains { + list.push(item_index.clone()); + } + + >::insert((collection_id, owner.clone()), list); + + } else { + + let mut itm = Vec::new(); + itm.push(item_index.clone()); + >::insert((collection_id, owner), itm); + } + + Ok(()) + } + + fn remove_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult { + + let list_exists = >::contains_key((collection_id, owner.clone())); + if list_exists { + + let mut list = >::get((collection_id, owner.clone())); + let item_contains = list.contains(&item_index.clone()); + + if item_contains { + list.retain(|&item| item != item_index); + >::insert((collection_id, owner), list); + } + } + + Ok(()) + } + + fn move_token_index(collection_id: u64, item_index: u64, old_owner: T::AccountId, new_owner: T::AccountId) -> DispatchResult { + + Self::remove_token_index(collection_id, item_index, old_owner)?; + Self::add_token_index(collection_id, item_index, new_owner)?; + + Ok(()) + } +} \ No newline at end of file --- a/pallets/nft/src/tests.rs +++ b/pallets/nft/src/tests.rs @@ -5,9 +5,14 @@ #[test] fn create_collection_test() { new_test_ext().execute_with(|| { + + let col_name1: Vec = "Test1\0".encode_utf16().collect::>(); + let col_desc1: Vec = "TestDescription1\0".encode_utf16().collect::>(); + let token_prefix1: Vec = b"token_prefix1\0".to_vec(); + let size = 1024; let origin1 = Origin::signed(1); - assert_ok!(TemplateModule::create_collection(origin1.clone(), size)); + assert_ok!(TemplateModule::create_collection(origin1.clone(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), size)); assert_eq!(TemplateModule::collection(1).owner, 1); }); } @@ -15,10 +20,15 @@ #[test] fn change_collection_owner() { new_test_ext().execute_with(|| { + + let col_name1: Vec = "Test1\0".encode_utf16().collect::>(); + let col_desc1: Vec = "TestDescription1\0".encode_utf16().collect::>(); + let token_prefix1: Vec = b"token_prefix1\0".to_vec(); + let size = 1024; let origin1 = Origin::signed(1); - assert_ok!(TemplateModule::create_collection(origin1.clone(), size)); + assert_ok!(TemplateModule::create_collection(origin1.clone(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), size)); assert_ok!(TemplateModule::change_collection_owner( origin1.clone(), 1, @@ -31,10 +41,15 @@ #[test] fn destroy_collection() { new_test_ext().execute_with(|| { + + let col_name1: Vec = "Test1\0".encode_utf16().collect::>(); + let col_desc1: Vec = "TestDescription1\0".encode_utf16().collect::>(); + let token_prefix1: Vec = b"token_prefix1\0".to_vec(); + let size = 1024; let origin1 = Origin::signed(1); - assert_ok!(TemplateModule::create_collection(origin1.clone(), size)); + assert_ok!(TemplateModule::create_collection(origin1.clone(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), size)); assert_ok!(TemplateModule::destroy_collection(origin1.clone(), 1)); }); } @@ -42,11 +57,16 @@ #[test] fn create_item() { new_test_ext().execute_with(|| { + + let col_name1: Vec = "Test1\0".encode_utf16().collect::>(); + let col_desc1: Vec = "TestDescription1\0".encode_utf16().collect::>(); + let token_prefix1: Vec = b"token_prefix1\0".to_vec(); + let size = 1024; let origin1 = Origin::signed(1); let origin2 = Origin::signed(2); - assert_ok!(TemplateModule::create_collection(origin1.clone(), size)); + assert_ok!(TemplateModule::create_collection(origin1.clone(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), size)); assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2)); assert_ok!(TemplateModule::create_item( origin2.clone(), @@ -62,11 +82,16 @@ #[test] fn burn_item() { new_test_ext().execute_with(|| { + + let col_name1: Vec = "Test1\0".encode_utf16().collect::>(); + let col_desc1: Vec = "TestDescription1\0".encode_utf16().collect::>(); + let token_prefix1: Vec = b"token_prefix1\0".to_vec(); + let size = 1024; let origin1 = Origin::signed(1); let origin2 = Origin::signed(2); - assert_ok!(TemplateModule::create_collection(origin1.clone(), size)); + assert_ok!(TemplateModule::create_collection(origin1.clone(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), size)); assert_ok!(TemplateModule::add_collection_admin(origin1.clone(), 1, 2)); assert_ok!(TemplateModule::create_item( origin2.clone(), @@ -91,14 +116,19 @@ #[test] fn add_collection_admin() { new_test_ext().execute_with(|| { + + let col_name1: Vec = "Test1\0".encode_utf16().collect::>(); + let col_desc1: Vec = "TestDescription1\0".encode_utf16().collect::>(); + let token_prefix1: Vec = b"token_prefix1\0".to_vec(); + let size = 1024; let origin1 = Origin::signed(1); let origin2 = Origin::signed(2); let origin3 = Origin::signed(3); - assert_ok!(TemplateModule::create_collection(origin1.clone(), size)); - assert_ok!(TemplateModule::create_collection(origin2.clone(), size)); - assert_ok!(TemplateModule::create_collection(origin3.clone(), size)); + assert_ok!(TemplateModule::create_collection(origin1.clone(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), size)); + assert_ok!(TemplateModule::create_collection(origin2.clone(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), size)); + assert_ok!(TemplateModule::create_collection(origin3.clone(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), size)); assert_eq!(TemplateModule::collection(1).owner, 1); assert_eq!(TemplateModule::collection(2).owner, 2); @@ -116,14 +146,19 @@ #[test] fn remove_collection_admin() { new_test_ext().execute_with(|| { + + let col_name1: Vec = "Test1\0".encode_utf16().collect::>(); + let col_desc1: Vec = "TestDescription1\0".encode_utf16().collect::>(); + let token_prefix1: Vec = b"token_prefix1\0".to_vec(); + let size = 1024; let origin1 = Origin::signed(1); let origin2 = Origin::signed(2); let origin3 = Origin::signed(3); - assert_ok!(TemplateModule::create_collection(origin1.clone(), size)); - assert_ok!(TemplateModule::create_collection(origin2.clone(), size)); - assert_ok!(TemplateModule::create_collection(origin3.clone(), size)); + assert_ok!(TemplateModule::create_collection(origin1.clone(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), size)); + assert_ok!(TemplateModule::create_collection(origin2.clone(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), size)); + assert_ok!(TemplateModule::create_collection(origin3.clone(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), size)); assert_eq!(TemplateModule::collection(1).owner, 1); assert_eq!(TemplateModule::collection(2).owner, 2); @@ -149,14 +184,19 @@ #[test] fn balance_of() { new_test_ext().execute_with(|| { + + let col_name1: Vec = "Test1\0".encode_utf16().collect::>(); + let col_desc1: Vec = "TestDescription1\0".encode_utf16().collect::>(); + let token_prefix1: Vec = b"token_prefix1\0".to_vec(); + let size = 1024; let origin1 = Origin::signed(1); let origin2 = Origin::signed(2); let origin3 = Origin::signed(3); - assert_ok!(TemplateModule::create_collection(origin1.clone(), size)); - assert_ok!(TemplateModule::create_collection(origin2.clone(), size)); - assert_ok!(TemplateModule::create_collection(origin3.clone(), size)); + assert_ok!(TemplateModule::create_collection(origin1.clone(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), size)); + assert_ok!(TemplateModule::create_collection(origin2.clone(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), size)); + assert_ok!(TemplateModule::create_collection(origin3.clone(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), size)); assert_eq!(TemplateModule::collection(1).owner, 1); assert_eq!(TemplateModule::collection(2).owner, 2); @@ -181,14 +221,19 @@ #[test] fn transfer() { new_test_ext().execute_with(|| { + + let col_name1: Vec = "Test1\0".encode_utf16().collect::>(); + let col_desc1: Vec = "TestDescription1\0".encode_utf16().collect::>(); + let token_prefix1: Vec = b"token_prefix1\0".to_vec(); + let size = 1024; let origin1 = Origin::signed(1); let origin2 = Origin::signed(2); let origin3 = Origin::signed(3); - assert_ok!(TemplateModule::create_collection(origin1.clone(), size)); - assert_ok!(TemplateModule::create_collection(origin2.clone(), size)); - assert_ok!(TemplateModule::create_collection(origin3.clone(), size)); + assert_ok!(TemplateModule::create_collection(origin1.clone(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), size)); + assert_ok!(TemplateModule::create_collection(origin2.clone(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), size)); + assert_ok!(TemplateModule::create_collection(origin3.clone(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), size)); assert_eq!(TemplateModule::collection(1).owner, 1); assert_eq!(TemplateModule::collection(2).owner, 2); @@ -214,14 +259,19 @@ #[test] fn approve() { new_test_ext().execute_with(|| { + + let col_name1: Vec = "Test1\0".encode_utf16().collect::>(); + let col_desc1: Vec = "TestDescription1\0".encode_utf16().collect::>(); + let token_prefix1: Vec = b"token_prefix1\0".to_vec(); + let size = 1024; let origin1 = Origin::signed(1); let origin2 = Origin::signed(2); let origin3 = Origin::signed(3); - assert_ok!(TemplateModule::create_collection(origin1.clone(), size)); - assert_ok!(TemplateModule::create_collection(origin2.clone(), size)); - assert_ok!(TemplateModule::create_collection(origin3.clone(), size)); + assert_ok!(TemplateModule::create_collection(origin1.clone(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), size)); + assert_ok!(TemplateModule::create_collection(origin2.clone(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), size)); + assert_ok!(TemplateModule::create_collection(origin3.clone(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), size)); assert_eq!(TemplateModule::collection(1).owner, 1); assert_eq!(TemplateModule::collection(2).owner, 2); @@ -243,14 +293,19 @@ #[test] fn get_approved() { new_test_ext().execute_with(|| { + + let col_name1: Vec = "Test1\0".encode_utf16().collect::>(); + let col_desc1: Vec = "TestDescription1\0".encode_utf16().collect::>(); + let token_prefix1: Vec = b"token_prefix1\0".to_vec(); + let size = 1024; let origin1 = Origin::signed(1); let origin2 = Origin::signed(2); let origin3 = Origin::signed(3); - assert_ok!(TemplateModule::create_collection(origin1.clone(), size)); - assert_ok!(TemplateModule::create_collection(origin2.clone(), size)); - assert_ok!(TemplateModule::create_collection(origin3.clone(), size)); + assert_ok!(TemplateModule::create_collection(origin1.clone(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), size)); + assert_ok!(TemplateModule::create_collection(origin2.clone(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), size)); + assert_ok!(TemplateModule::create_collection(origin3.clone(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), size)); assert_eq!(TemplateModule::collection(1).owner, 1); assert_eq!(TemplateModule::collection(2).owner, 2); @@ -272,14 +327,19 @@ #[test] fn transfer_from() { new_test_ext().execute_with(|| { + + let col_name1: Vec = "Test1\0".encode_utf16().collect::>(); + let col_desc1: Vec = "TestDescription1\0".encode_utf16().collect::>(); + let token_prefix1: Vec = b"token_prefix1\0".to_vec(); + let size = 1024; let origin1 = Origin::signed(1); let origin2 = Origin::signed(2); let origin3 = Origin::signed(3); - assert_ok!(TemplateModule::create_collection(origin1.clone(), size)); - assert_ok!(TemplateModule::create_collection(origin2.clone(), size)); - assert_ok!(TemplateModule::create_collection(origin3.clone(), size)); + assert_ok!(TemplateModule::create_collection(origin1.clone(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), size)); + assert_ok!(TemplateModule::create_collection(origin2.clone(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), size)); + assert_ok!(TemplateModule::create_collection(origin3.clone(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), size)); assert_eq!(TemplateModule::collection(1).owner, 1); assert_eq!(TemplateModule::collection(2).owner, 2); @@ -297,3 +357,50 @@ assert_ok!(TemplateModule::transfer_from(origin1.clone(), 1, 1, 2)); }); } + +#[test] +fn index_list() { + new_test_ext().execute_with(|| { + + let col_name1: Vec = "Test1\0".encode_utf16().collect::>(); + let col_desc1: Vec = "TestDescription1\0".encode_utf16().collect::>(); + let token_prefix1: Vec = b"token_prefix1\0".to_vec(); + + let size = 1024; + let origin1 = Origin::signed(1); + let origin2 = Origin::signed(2); + let origin3 = Origin::signed(3); + + assert_ok!(TemplateModule::create_collection(origin1.clone(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), size)); + assert_ok!(TemplateModule::create_collection(origin2.clone(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), size)); + assert_ok!(TemplateModule::create_collection(origin3.clone(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), size)); + + assert_eq!(TemplateModule::collection(1).owner, 1); + assert_eq!(TemplateModule::collection(2).owner, 2); + assert_eq!(TemplateModule::collection(3).owner, 3); + // create items + assert_ok!(TemplateModule::create_item( + origin1.clone(), + 1, + [1, 1, 1].to_vec() + )); + assert_ok!(TemplateModule::create_item( + origin1.clone(), + 1, + [1, 1, 2].to_vec() + )); + assert_ok!(TemplateModule::create_item( + origin1.clone(), + 1, + [1, 2, 3].to_vec() + )); + assert_eq!(TemplateModule::address_tokens((1, 1)).len(), 3); + // burn one + assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 2)); + assert_eq!(TemplateModule::address_tokens((1, 1)).len(), 2); + // burn another one + assert_ok!(TemplateModule::burn_item(origin1.clone(), 1, 3)); + assert_eq!(TemplateModule::address_tokens((1, 1))[0], 1); + }); +} + -- gitstuff