git.delta.rocks / unique-network / refs/commits / 31f605387c25

difftreelog

NFTPAR-96: Batch Minting.

sotmorskiy2020-10-16parent: #0dbd5dd.patch.diff
in: master

2 files changed

modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
--- a/pallets/nft/src/lib.rs
+++ b/pallets/nft/src/lib.rs
@@ -10,7 +10,7 @@
 pub use frame_support::{
     construct_runtime, decl_event, decl_module, decl_storage,
     dispatch::DispatchResult,
-    ensure, parameter_types,
+    ensure, fail, parameter_types,
     traits::{
         Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,
         Randomness, WithdrawReason,
@@ -747,68 +747,53 @@
         pub fn create_item(origin, collection_id: u64, properties: Vec<u8>, owner: T::AccountId) -> DispatchResult {
 
             let sender = ensure_signed(origin)?;
-            Self::collection_exists(collection_id)?;
-            let target_collection = <Collection<T>>::get(collection_id);
-
-            if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {
-                ensure!(target_collection.mint_mode == true, "Public minting is not allowed for this collection");
-                Self::check_white_list(collection_id, &owner)?;
-                Self::check_white_list(collection_id, &sender)?;
-            }
 
-            match target_collection.mode
-            {
-                CollectionMode::NFT(_) => {
+            Self::collection_exists(collection_id)?;
 
-                    // check size
-                    ensure!(target_collection.custom_data_size >= properties.len() as u32, "Size of item is too large");
+            let target_collection = <Collection<T>>::get(collection_id);
 
-                    // Create nft item
-                    let item = NftItemType {
-                        collection: collection_id,
-                        owner: owner,
-                        data: properties.clone(),
-                    };
-
-                    Self::add_nft_item(item)?;
-
-                },
-                CollectionMode::Fungible(_) => {
-
-                    // check size
-                    ensure!(properties.len() as u32 == 0, "Size of item must be 0 with fungible type");
+            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;
+            Self::validate_create_item_args(&target_collection, &properties)?;
+            Self::create_item_no_validation(collection_id, &target_collection, &properties, &owner)?;
 
-                    let item = FungibleItemType {
-                        collection: collection_id,
-                        owner: owner,
-                        value: (10 as u128).pow(target_collection.decimal_points)
-                    };
-
-                    Self::add_fungible_item(item)?;
-                },
-                CollectionMode::ReFungible(_, _) => {
-
-                    // check size
-                    ensure!(target_collection.custom_data_size >= properties.len() as u32, "Size of item is too large");
+            Ok(())
+        }
 
-                    let mut owner_list = Vec::new();
-                    let value = (10 as u128).pow(target_collection.decimal_points);
-                    owner_list.push(Ownership {owner: owner.clone(), fraction: value});
+        /// This method creates multiple instances of NFT Collection created with CreateCollection method.
+        /// 
+        /// # Permissions
+        /// 
+        /// * Collection Owner.
+        /// * Collection Admin.
+        /// * Anyone if
+        ///     * White List is enabled, and
+        ///     * Address is added to white list, and
+        ///     * MintPermission is enabled (see SetMintPermission method)
+        /// 
+        /// # Arguments
+        /// 
+        /// * collection_id: ID of the collection.
+        /// 
+        /// * properties: Array items properties. Each property is an array of bytes itself, see [create_item].
+        /// 
+        /// * owner: Address, initial owner of the NFT.
+        #[weight = 0]
+        pub fn create_multiple_items(origin, collection_id: u64, properties: Vec<Vec<u8>>, owner: T::AccountId) -> DispatchResult {
 
-                    let item = ReFungibleItemType {
-                        collection: collection_id,
-                        owner: owner_list,
-                        data: properties.clone()
-                    };
+            ensure!(properties.len() > 0, "Length of items properties must be greater than 0.");
+            let sender = ensure_signed(origin)?;
 
-                    Self::add_refungible_item(item)?;
-                },
-                _ => { ensure!(1 == 0,"just error"); }
+            Self::collection_exists(collection_id)?;
+            let target_collection = <Collection<T>>::get(collection_id);
 
-            };
+            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;
 
-            // call event
-            Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));
+            for prop in &properties {
+                Self::validate_create_item_args(&target_collection, prop)?;
+            }
+            for prop in &properties {
+                Self::create_item_no_validation(collection_id, &target_collection, prop, &owner)?;
+            }
 
             Ok(())
         }
@@ -1078,6 +1063,94 @@
 }
 
 impl<T: Trait> Module<T> {
+
+    fn can_create_items_in_collection(collection_id: u64, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {
+
+        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {
+            ensure!(collection.mint_mode == true, "Public minting is not allowed for this collection");
+            Self::check_white_list(collection_id, owner)?;
+            Self::check_white_list(collection_id, sender)?;
+        }
+
+        Ok(())
+    }
+
+    fn validate_create_item_args(collection: &CollectionType<T::AccountId>, properties: &Vec<u8>) -> DispatchResult {
+
+        match collection.mode
+        {
+            CollectionMode::NFT(_) => {
+
+                // check size
+                ensure!(collection.custom_data_size >= properties.len() as u32, "Size of item is too large")
+            },
+            CollectionMode::Fungible(_) => {
+
+                // check size
+                ensure!(properties.len() as u32 == 0, "Size of item must be 0 with fungible type")
+            },
+            CollectionMode::ReFungible(_, _) => {
+
+                // check size
+                ensure!(collection.custom_data_size >= properties.len() as u32, "Size of item is too large")
+            },
+            _ => {
+                fail!("Unexpected collection mode")
+            }
+        }
+
+        Ok(())
+    }
+
+    fn create_item_no_validation(collection_id: u64, collection: &CollectionType<T::AccountId>, properties: &Vec<u8>, owner: &T::AccountId) -> DispatchResult {
+        match collection.mode
+        {
+            CollectionMode::NFT(_) => {
+
+                // Create nft item
+                let item = NftItemType {
+                    collection: collection_id,
+                    owner: owner.clone(),
+                    data: properties.clone(),
+                };
+
+                Self::add_nft_item(item)?;
+
+            },
+            CollectionMode::Fungible(_) => {
+
+                let item = FungibleItemType {
+                    collection: collection_id,
+                    owner: owner.clone(),
+                    value: (10 as u128).pow(collection.decimal_points)
+                };
+
+                Self::add_fungible_item(item)?;
+            },
+            CollectionMode::ReFungible(_, _) => {
+
+                let mut owner_list = Vec::new();
+                let value = (10 as u128).pow(collection.decimal_points);
+                owner_list.push(Ownership {owner: owner.clone(), fraction: value});
+
+                let item = ReFungibleItemType {
+                    collection: collection_id,
+                    owner: owner_list,
+                    data: properties.clone()
+                };
+
+                Self::add_refungible_item(item)?;
+            },
+            _ => { ensure!(1 == 0,"just error"); }
+
+        };
+
+        // call event
+        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));
+
+        Ok(())
+    }
+
     fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {
         let current_index = <ItemListIndex>::get(item.collection)
             .checked_add(1)
modifiedpallets/nft/src/tests.rsdiffbeforeafterboth
44 });44 });
45}45}
46
47// Use cases tests region
48// #region
49#[test]
50fn create_nft_multiple_items() {
51 new_test_ext().execute_with(|| {
52 let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
53 let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
54 let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
55 let mode: CollectionMode = CollectionMode::NFT(2000);
56
57 assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
58 collection_numbers_limit: 10,
59 account_token_ownership_limit: 10,
60 collections_admins_limit: 5,
61 custom_data_limit: 2048,
62 nft_sponsor_transfer_timeout: 15,
63 fungible_sponsor_transfer_timeout: 15,
64 refungible_sponsor_transfer_timeout: 15,
65 }));
66
67 let origin1 = Origin::signed(1);
68 assert_ok!(TemplateModule::create_collection(
69 origin1.clone(),
70 col_name1.clone(),
71 col_desc1.clone(),
72 token_prefix1.clone(),
73 mode
74 ));
75 assert_eq!(TemplateModule::collection(1).owner, 1);
76
77 let properties = [[1, 2, 3].to_vec(), [3, 2, 1].to_vec(), [3, 3, 3].to_vec()].to_vec();
78
79 assert_ok!(TemplateModule::create_multiple_items(
80 origin1.clone(),
81 1,
82 properties.clone(),
83 1
84 ));
85 for (index, data) in properties.iter().enumerate() {
86 assert_eq!(TemplateModule::nft_item_id(1, (index + 1) as u64).data, *data);
87 }
88 });
89}
4690
47#[test]91#[test]
48fn create_refungible_item() {92fn create_refungible_item() {
93}137}
94138
95#[test]139#[test]
96fn create_fungible_item() {140fn create_multiple_refungible_items() {
141 new_test_ext().execute_with(|| {
142 let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
143 let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
144 let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
145 let mode: CollectionMode = CollectionMode::ReFungible(2000, 3);
146
147 assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
148 collection_numbers_limit: 10,
149 account_token_ownership_limit: 10,
150 collections_admins_limit: 5,
151 custom_data_limit: 2048,
152 nft_sponsor_transfer_timeout: 15,
153 fungible_sponsor_transfer_timeout: 15,
154 refungible_sponsor_transfer_timeout: 15,
155 }));
156
157 let origin1 = Origin::signed(1);
158 assert_ok!(TemplateModule::create_collection(
159 origin1.clone(),
160 col_name1.clone(),
161 col_desc1.clone(),
162 token_prefix1.clone(),
163 mode
164 ));
165 assert_eq!(TemplateModule::collection(1).owner, 1);
166
167 let properties = [[1, 2, 3].to_vec(), [3, 2, 1].to_vec(), [3, 3, 3].to_vec()].to_vec();
168
169 assert_ok!(TemplateModule::create_multiple_items(
170 origin1.clone(),
171 1,
172 properties.clone(),
173 1
174 ));
175 for (index, data) in properties.iter().enumerate() {
176
177 let item = TemplateModule::refungible_item_id(1, (index + 1) as u64);
178 assert_eq!(item.data, *data);
179 assert_eq!(
180 item.owner[0],
181 Ownership {
182 owner: 1,
183 fraction: 1000
184 }
185 );
186 }
187 });
188}
189
190#[test]
191fn create_fungible_item() {
192 new_test_ext().execute_with(|| {
193 let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
194 let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
195 let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
196 let mode: CollectionMode = CollectionMode::Fungible(3);
197
198 assert_ok!(TemplateModule::set_chain_limits(RawOrigin::Root.into(), ChainLimits {
199 collection_numbers_limit: 10,
200 account_token_ownership_limit: 10,
201 collections_admins_limit: 5,
202 custom_data_limit: 2048,
203 nft_sponsor_transfer_timeout: 15,
204 fungible_sponsor_transfer_timeout: 15,
205 refungible_sponsor_transfer_timeout: 15,
206 }));
207
208 let origin1 = Origin::signed(1);
209 assert_ok!(TemplateModule::create_collection(
210 origin1.clone(),
211 col_name1.clone(),
212 col_desc1.clone(),
213 token_prefix1.clone(),
214 mode
215 ));
216 assert_eq!(TemplateModule::collection(1).owner, 1);
217
218 assert_ok!(TemplateModule::create_item(
219 origin1.clone(),
220 1,
221 [].to_vec(),
222 1
223 ));
224 assert_eq!(TemplateModule::fungible_item_id(1, 1).owner, 1);
225 });
226}
227
228#[test]
229fn create_multiple_fungible_items() {
97 new_test_ext().execute_with(|| {230 new_test_ext().execute_with(|| {
98 let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();231 let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
99 let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();232 let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
120 ));253 ));
121 assert_eq!(TemplateModule::collection(1).owner, 1);254 assert_eq!(TemplateModule::collection(1).owner, 1);
255
256 let properties = [[].to_vec(), [].to_vec(), [].to_vec()].to_vec();
122257
123 assert_ok!(TemplateModule::create_item(258 assert_ok!(TemplateModule::create_multiple_items(
124 origin1.clone(),259 origin1.clone(),
125 1,260 1,
126 [].to_vec(),261 properties.clone(),
127 1262 1
128 ));263 ));
264
265 for (index, _) in properties.iter().enumerate() {
129 assert_eq!(TemplateModule::fungible_item_id(1, 1).owner, 1);266 assert_eq!(TemplateModule::fungible_item_id(1, (index + 1) as u64).owner, 1);
267 }
130 assert_eq!(TemplateModule::balance_count(1, 1), 1000);268 assert_eq!(TemplateModule::balance_count(1, 1), 3000);
131 assert_eq!(TemplateModule::address_tokens(1, 1), [1]);269 assert_eq!(TemplateModule::address_tokens(1, 1), [1, 2, 3]);
132 });270 });
133}271}
134272