difftreelog
Refactoring
in: master
3 files changed
doc/demo_milestone1-2.mddiffbeforeafterboth525253Before running test, open [chain state tab](https://polkadot.js.org/apps/#/chainstate) and read `nft`.`nextCollectionId` state variable, which shows how many collections were created so far. If you just started the chain, this should equal 0. 53Before running test, open [chain state tab](https://polkadot.js.org/apps/#/chainstate) and read `nft`.`nextCollectionId` state variable, which shows how many collections were created so far. If you just started the chain, this should equal 0. 545455Open extrinsics tab of the [standard UI](https://polkadot.js.org/apps/#/extrinsics) and select ALICE account. Select `nft` module and `createCollection` method. Put 1 in `custom_data_sz` and run the transaction. After it is finalized, read the `nft`.`nextCollectionId` state variable. It will be equal 1.55Open extrinsics tab of the [standard UI](https://polkadot.js.org/apps/#/extrinsics) and select ALICE account. Select `nft` module and `createCollection` method. Put 1 in `custom_data_size` and run the transaction. After it is finalized, read the `nft`.`nextCollectionId` state variable. It will be equal 1.565657Also, read the state variable `nft`.`collection` with ID = 1 (Because everything in NFT Palette is numbered from 1, not from 0). You will see something like this:57Also, read the state variable `nft`.`collection` with ID = 1 (Because everything in NFT Palette is numbered from 1, not from 0). You will see something like this:5858pallets/nft/src/lib.rsdiffbeforeafterboth1#![cfg_attr(not(feature = "std"), no_std)]1#![cfg_attr(not(feature = "std"), no_std)]223use codec::{Decode, Encode};4/// A FRAME pallet template with necessary imports56/// Feel free to remove or edit this file as needed.7/// If you change the name of this file, make sure to update its references in runtime/src/lib.rs8/// If you remove this file, you can remove those references910/// For more guidance on Substrate FRAME, see the example pallet3/// For more guidance on Substrate FRAME, see the example pallet11/// https://github.com/paritytech/substrate/blob/master/frame/example/src/lib.rs4/// https://github.com/paritytech/substrate/blob/master/frame/example/src/lib.rs56use codec::{Decode, Encode};12use frame_support::{decl_event, decl_module, decl_storage, dispatch::DispatchResult, ensure};7use frame_support::{decl_event, decl_module, decl_storage, dispatch::DispatchResult, ensure};13use frame_system::{self as system, ensure_signed};8use frame_system::{self as system, ensure_signed};14use sp_runtime::sp_std::prelude::Vec;9use sp_runtime::sp_std::prelude::Vec;19#[cfg(test)]14#[cfg(test)]20mod tests;15mod tests;211617#[derive(Encode, Decode, Debug, Clone, PartialEq)]18pub enum AccessMode {19 Normal,20 WhiteList,21}22impl Default for AccessMode { fn default() -> Self { Self::Normal } }2324#[derive(Encode, Decode, Debug, Eq, Clone, PartialEq)]25pub enum CollectionMode {26 Invalid,27 NFT,28 Fungible,29 ReFungible,30}31impl Default for CollectionMode { fn default() -> Self { Self::Invalid } }3222#[derive(Encode, Decode, Default, Clone, PartialEq)]33#[derive(Encode, Decode, Default, Clone, PartialEq)]23#[cfg_attr(feature = "std", derive(Debug))]34#[cfg_attr(feature = "std", derive(Debug))]35pub struct Ownership<AccountId> {36 pub owner: AccountId,37 pub fraction: u12838}3940#[derive(Encode, Decode, Default, Clone, PartialEq)]41#[cfg_attr(feature = "std", derive(Debug))]24pub struct CollectionType<AccountId> {42pub struct CollectionType<AccountId> {25 pub owner: AccountId,43 pub owner: AccountId,44 pub mode: CollectionMode,45 pub access: AccessMode,26 pub next_item_id: u64,46 pub next_item_id: u64,47 pub decimal_points: u32,27 pub name: Vec<u16>, // 64 include null escape char48 pub name: Vec<u16>, // 64 include null escape char28 pub description: Vec<u16>, // 256 include null escape char49 pub description: Vec<u16>, // 256 include null escape char29 pub token_prefix: Vec<u8>, // 16 include null escape char50 pub token_prefix: Vec<u8>, // 16 include null escape char45 pub data: Vec<u8>,66 pub data: Vec<u8>,46}67}476848/// The pallet's configuration trait.69#[derive(Encode, Decode, Default, Clone, PartialEq)]70#[cfg_attr(feature = "std", derive(Debug))]71pub struct FungibleItemType<AccountId> {72 pub collection: u64,73 pub owner: Vec<AccountId>,74 pub data: Vec<u64>,75}7677#[derive(Encode, Decode, Default, Clone, PartialEq)]78#[cfg_attr(feature = "std", derive(Debug))]79pub struct ReFungibleItemType<AccountId> {80 pub collection: u64,81 pub owner: Vec<Ownership<AccountId>>,82}8349pub trait Trait: system::Trait {84pub trait Trait: system::Trait {50 // Add other types and constants required to configure this pallet.5152 /// The overarching event type.53 type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;85 type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;54}86}558756// This pallet's storage items.57decl_storage! {88decl_storage! {58 // It is important to update your storage name so that your pallet's59 // storage items are isolated from other pallets.60 trait Store for Module<T: Trait> as Nft {89 trait Store for Module<T: Trait> as Nft {619062 /// Next available collection ID91 // Next available collection ID63 pub NextCollectionID get(fn next_collection_id): u64;92 NextCollectionID get(fn next_collection_id): u64;64 pub Collection get(fn collection): map hasher(identity) u64 => CollectionType<T::AccountId>;93 pub Collection get(fn collection): map hasher(identity) u64 => CollectionType<T::AccountId>;65 pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;94 pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;669567 /// Balance owner per collection map96 // Balance owner per collection map68 pub Balance get(fn balance_count): map hasher(blake2_128_concat) (u64, T::AccountId) => u64;97 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;69 pub ApprovedList get(fn approved): map hasher(blake2_128_concat) (u64, u64) => Vec<T::AccountId>;98 pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => Vec<T::AccountId>;709971 pub ItemList get(fn item_id): map hasher(blake2_128_concat) (u64, u64) => NftItemType<T::AccountId>;100 // Item collections101 pub NftItemList get(fn nft_item_id): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;72 pub ItemListIndex get(fn item_index): map hasher(blake2_128_concat) u64 => u64;102 pub FungibleItemList get(fn fungible_item_id): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;103 pub ReFungibleItemList get(fn refungible_item_id): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;104 ItemListIndex get(fn item_index): map hasher(blake2_128_concat) u64 => u64;7310574 pub AddressTokens get(fn address_tokens): map hasher(blake2_128_concat) (u64, T::AccountId) => Vec<u64>;106 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;75 }107 }76}108}7710978// The pallet's events79decl_event!(110decl_event!(80 pub enum Event<T>111 pub enum Event<T>81 where112 where82 AccountId = <T as system::Trait>::AccountId,113 AccountId = <T as system::Trait>::AccountId,83 {114 {84 Created(u64, AccountId),115 Created(u64, CollectionMode, AccountId),85 ItemCreated(u64, u64),116 ItemCreated(u64, u64),86 ItemDestroyed(u64, u64),117 ItemDestroyed(u64, u64),87 }118 }88);119);8912090// The pallet's dispatchable functions.91decl_module! {121decl_module! {92 /// The module declaration.93 pub struct Module<T: Trait> for enum Call where origin: T::Origin {122 pub struct Module<T: Trait> for enum Call where origin: T::Origin {9412395 // Initializing events96 // this is needed only if you are using events in your pallet97 fn deposit_event() = default;124 fn deposit_event() = default;9812599 // Create collection of NFT with given parameters126 // Create collection of NFT with given parameters105 collection_name: Vec<u16>,132 collection_name: Vec<u16>,106 collection_description: Vec<u16>,133 collection_description: Vec<u16>,107 token_prefix: Vec<u8>,134 token_prefix: Vec<u8>,108 custom_data_sz: u32) -> DispatchResult {135 mode: CollectionMode,136 decimal_points: u32,137 custom_data_size: u32) -> DispatchResult {109138110 // Anyone can create a collection139 // Anyone can create a collection111 let who = ensure_signed(origin)?;140 let who = ensure_signed(origin)?;112141142 // check type143 ensure!((mode == CollectionMode::Fungible || mode == CollectionMode::NFT || mode == CollectionMode::ReFungible), 144 "Collection mode must be Fungible, NFT or ReFungible"); 145146 // NFT checks147 if mode == CollectionMode::NFT148 {149 ensure!(decimal_points == 0, "Collection in NFT mode must have zero in decimal_points parameter"); 150 }151152 // Fungible checks153 if mode == CollectionMode::Fungible154 {155 ensure!(custom_data_size == 0, "Collection in Fungible mode must have zero in custom_data_size parameter"); 156 ensure!(decimal_points != 0, "Collection in Fungible mode must have not zero in decimal_points parameter"); 157 }158113 // check params159 // check params160 ensure!(decimal_points < 100, "decimal_points parameter must be lower than 100"); 161114 let mut name = collection_name.to_vec();162 let mut name = collection_name.to_vec();115 name.push(0);163 name.push(0);116 ensure!(name.len() <= 64, "Collection name can not be longer than 63 char");164 ensure!(name.len() <= 64, "Collection name can not be longer than 63 char");134 let new_collection = CollectionType {182 let new_collection = CollectionType {135 owner: who.clone(),183 owner: who.clone(),136 name: name,184 name: name,185 mode: mode.clone(),186 access: AccessMode::Normal,137 description: description,187 description: description,188 decimal_points: decimal_points,138 token_prefix: prefix,189 token_prefix: prefix,139 next_item_id: next_id,190 next_item_id: next_id,140 custom_data_size: custom_data_sz,191 custom_data_size: custom_data_size,141 };192 };142193143 // Add new collection to map194 // Add new collection to map144 <Collection<T>>::insert(next_id, new_collection);195 <Collection<T>>::insert(next_id, new_collection);145196146 // call event197 // call event147 Self::deposit_event(RawEvent::Created(next_id, who.clone()));198 Self::deposit_event(RawEvent::Created(next_id, mode.clone(), who.clone()));148199149 Ok(())200 Ok(())150 }201 }153 pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {204 pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {154205155 let sender = ensure_signed(origin)?;206 let sender = ensure_signed(origin)?;156 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");207 Self::check_owner_permissions(collection_id, sender)?;157208158 let owner = <Collection<T>>::get(collection_id).owner;209 <AddressTokens<T>>::remove_prefix(collection_id);210 <ApprovedList<T>>::remove_prefix(collection_id);159 ensure!(sender == owner, "You do not own this collection");211 <Balance<T>>::remove_prefix(collection_id);212 <ItemListIndex>::remove(collection_id);213 <AdminList<T>>::remove(collection_id);160 <Collection<T>>::remove(collection_id);214 <Collection<T>>::remove(collection_id);161215162 Ok(())216 Ok(())166 pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {220 pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {167221168 let sender = ensure_signed(origin)?;222 let sender = ensure_signed(origin)?;169 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");223 Self::check_owner_permissions(collection_id, sender)?;170171 let mut target_collection = <Collection<T>>::get(collection_id);224 let mut target_collection = <Collection<T>>::get(collection_id);172 ensure!(sender == target_collection.owner, "You do not own this collection");173174 target_collection.owner = new_owner;225 target_collection.owner = new_owner;175 <Collection<T>>::insert(collection_id, target_collection);226 <Collection<T>>::insert(collection_id, target_collection);176227181 pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {232 pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {182233183 let sender = ensure_signed(origin)?;234 let sender = ensure_signed(origin)?;184 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");235 Self::check_owner_or_admin_permissions(collection_id, sender)?;236 let mut admin_arr: Vec<T::AccountId> = Vec::new();185237186 let target_collection = <Collection<T>>::get(collection_id);238 if <AdminList<T>>::contains_key(collection_id)187 let is_owner = sender == target_collection.owner;188189 let no_perm_mes = "You do not have permissions to modify this collection";190 let exists = <AdminList<T>>::contains_key(collection_id);191192 if !is_owner193 {239 {194 ensure!(exists, no_perm_mes);195 ensure!(<AdminList<T>>::get(collection_id).contains(&sender), no_perm_mes);196 }197198 let mut admin_arr: Vec<T::AccountId> = Vec::new();199 if exists200 {201 admin_arr = <AdminList<T>>::get(collection_id);240 admin_arr = <AdminList<T>>::get(collection_id);202 ensure!(!admin_arr.contains(&new_admin_id), "Account already has admin role");241 ensure!(!admin_arr.contains(&new_admin_id), "Account already has admin role");203 }242 }212 pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {251 pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {213252214 let sender = ensure_signed(origin)?;253 let sender = ensure_signed(origin)?;215 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");254 Self::check_owner_or_admin_permissions(collection_id, sender)?;216255217 let target_collection = <Collection<T>>::get(collection_id);256 if <AdminList<T>>::contains_key(collection_id)218 let is_owner = sender == target_collection.owner;219220 let no_perm_mes = "You do not have permissions to modify this collection";221 let exists = <AdminList<T>>::contains_key(collection_id);222223 if !is_owner224 {257 {225 ensure!(exists, no_perm_mes);226 ensure!(<AdminList<T>>::get(collection_id).contains(&sender), no_perm_mes);227 }228229 if exists230 {231 let mut admin_arr = <AdminList<T>>::get(collection_id);258 let mut admin_arr = <AdminList<T>>::get(collection_id);232 admin_arr.retain(|i| *i != account_id);259 admin_arr.retain(|i| *i != account_id);233 <AdminList<T>>::insert(collection_id, admin_arr);260 <AdminList<T>>::insert(collection_id, admin_arr);237 }264 }238265239 #[weight = 0]266 #[weight = 0]240 pub fn create_item(origin, collection_id: u64, properties: Vec<u8>) -> DispatchResult {267 pub fn create_item(origin, collection_id: u64, properties: Vec<u8>, owner: T::AccountId) -> DispatchResult {241268242 let sender = ensure_signed(origin)?;269 let sender = ensure_signed(origin)?;243 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");244270271 // check size245 let target_collection = <Collection<T>>::get(collection_id);272 let target_collection = <Collection<T>>::get(collection_id);246 ensure!(target_collection.custom_data_size >= properties.len() as u32, "Size of item is too large");273 ensure!(target_collection.custom_data_size >= properties.len() as u32, "Size of item is too large");247 let is_owner = sender == target_collection.owner;248274249 let no_perm_mes = "You do not have permissions to modify this collection";275 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;250 let exists = <AdminList<T>>::contains_key(collection_id);251276252 if !is_owner277 let new_balance = <Balance<T>>::get(collection_id, sender.clone()) + 1;253 {254 ensure!(exists, no_perm_mes);255 ensure!(<AdminList<T>>::get(collection_id).contains(&sender), no_perm_mes);278 <Balance<T>>::insert(collection_id, sender.clone(), new_balance);256 }257279258 let new_balance = <Balance<T>>::get((collection_id, sender.clone())) + 1;280 // TODO: implement other modes259 <Balance<T>>::insert((collection_id, sender.clone()), new_balance);281 ensure!(target_collection.mode == CollectionMode::NFT, "Collection type not implemented");260282261 // Create new item283 if target_collection.mode == CollectionMode::NFT284 {285 // Create nft item262 let new_item = NftItemType {286 let item = NftItemType {263 collection: collection_id,287 collection: collection_id,264 owner: sender,288 owner: owner,265 data: properties,289 data: properties,266 };290 };267291292 Self::add_nft_item(item)?;293 }268294269 let current_index = <ItemListIndex>::get(collection_id)270 .checked_add(1)271 .expect("Item list index id error");272273 Self::add_token_index(collection_id, current_index, new_item.owner.clone())?;274275 <ItemListIndex>::insert(collection_id, current_index);276 <ItemList<T>>::insert((collection_id, current_index), new_item);277278 // call event295 // call event279 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index));296 Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));280297281 Ok(())298 Ok(())282 }299 }285 pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {302 pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {286303287 let sender = ensure_signed(origin)?;304 let sender = ensure_signed(origin)?;288 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");305 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;306 Self::burn_nft_item(collection_id, item_id)?;289307290 let target_collection = <Collection<T>>::get(collection_id);291 let is_owner = sender == target_collection.owner;292293 ensure!(<ItemList<T>>::contains_key((collection_id, item_id)), "Item does not exists");294 let item = <ItemList<T>>::get((collection_id, item_id));295296 if !is_owner297 {298 // check if item owner299 if item.owner != sender300 {301 let no_perm_mes = "You do not have permissions to modify this collection";302303 ensure!(<AdminList<T>>::contains_key(collection_id), no_perm_mes);304 ensure!(<AdminList<T>>::get(collection_id).contains(&sender), no_perm_mes);305 }306 }307 <ItemList<T>>::remove((collection_id, item_id));308309 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;310311 // update balance312 let new_balance = <Balance<T>>::get((collection_id, item.owner.clone())) - 1;313 <Balance<T>>::insert((collection_id, item.owner.clone()), new_balance);314315 // call event308 // call event316 Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));309 Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));317310318 Ok(())311 Ok(())319 }312 }320313321 #[weight = 0]314 #[weight = 0]322 pub fn transfer(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {315 pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {323316324 let sender = ensure_signed(origin)?;317 let sender = ensure_signed(origin)?;325 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");318 Self::check_owner_or_admin_permissions(collection_id, sender)?;326319327 let target_collection = <Collection<T>>::get(collection_id);320 let target_collection = <Collection<T>>::get(collection_id);328 let is_owner = sender == target_collection.owner;329321330 ensure!(<ItemList<T>>::contains_key((collection_id, item_id)), "Item does not exists");322 // TODO: implement other modes331 let mut item = <ItemList<T>>::get((collection_id, item_id));323 ensure!(target_collection.mode == CollectionMode::NFT, "Collection type not implemented");332324333 if !is_owner325 if target_collection.mode == CollectionMode::NFT334 {326 {335 // check if item owner327 Self::transfer_nft(collection_id, item_id, recipient)?;336 if item.owner != sender337 {338 let no_perm_mes = "You do not have permissions to modify this collection";339340 ensure!(<AdminList<T>>::contains_key(collection_id), no_perm_mes);341 ensure!(<AdminList<T>>::get(collection_id).contains(&sender), no_perm_mes);342 }343 }328 }344 <ItemList<T>>::remove((collection_id, item_id));345329346 // update balance347 let balance_old_owner = <Balance<T>>::get((collection_id, item.owner.clone())) - 1;348 <Balance<T>>::insert((collection_id, item.owner.clone()), balance_old_owner);349350 let balance_new_owner = <Balance<T>>::get((collection_id, new_owner.clone())) + 1;351 <Balance<T>>::insert((collection_id, new_owner.clone()), balance_new_owner);352353 // change owner354 let old_owner = item.owner.clone();355 item.owner = new_owner.clone();356 <ItemList<T>>::insert((collection_id, item_id), item);357358 // update index collection359 Self::move_token_index(collection_id, item_id, old_owner, new_owner.clone())?;360361 // reset approved list362 let itm: Vec<T::AccountId> = Vec::new();363 <ApprovedList<T>>::insert((collection_id, item_id), itm);364365 Ok(())330 Ok(())366 }331 }367332368 #[weight = 0]333 #[weight = 0]369 pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {334 pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {370335371 let sender = ensure_signed(origin)?;336 let sender = ensure_signed(origin)?;372 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");373337374 let target_collection = <Collection<T>>::get(collection_id);338 let item_owner = Self::is_item_owner(sender.clone(), collection_id, item_id);375 let is_owner = sender == target_collection.owner;376377 ensure!(<ItemList<T>>::contains_key((collection_id, item_id)), "Item does not exists");378 let item = <ItemList<T>>::get((collection_id, item_id));379380 if !is_owner339 if !item_owner381 {340 {382 // check if item owner341 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;383 if item.owner != sender384 {385 let no_perm_mes = "You do not have permissions to modify this collection";386387 ensure!(<AdminList<T>>::contains_key(collection_id), no_perm_mes);388 ensure!(<AdminList<T>>::get(collection_id).contains(&sender), no_perm_mes);389 }390 }342 }391343392 let list_exists = <ApprovedList<T>>::contains_key((collection_id, item_id));344 let list_exists = <ApprovedList<T>>::contains_key(collection_id, item_id);393 if list_exists {345 if list_exists {394346395 let mut list = <ApprovedList<T>>::get((collection_id, item_id));347 let mut list = <ApprovedList<T>>::get(collection_id, item_id);396 let item_contains = list.contains(&approved.clone());348 let item_contains = list.contains(&approved.clone());397349398 if !item_contains {350 if !item_contains {402354403 let mut itm = Vec::new();355 let mut itm = Vec::new();404 itm.push(approved.clone());356 itm.push(approved.clone());405 <ApprovedList<T>>::insert((collection_id, item_id), itm);357 <ApprovedList<T>>::insert(collection_id, item_id, itm);406 }358 }407359408 Ok(())360 Ok(())409 }361 }410362411 #[weight = 0]363 #[weight = 0]412 pub fn transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {364 pub fn transfer_from(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, ) -> DispatchResult {413365414 let no_perm_mes = "You do not have permissions to modify this collection";366 let mut approved: bool = false; 367 let sender = ensure_signed(origin)?;415 ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);368 let approved_list_exists = <ApprovedList<T>>::contains_key(collection_id, item_id);416 let list_itm = <ApprovedList<T>>::get((collection_id, item_id));369 if approved_list_exists370 {371 let list_itm = <ApprovedList<T>>::get(collection_id, item_id);417 ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);372 approved = list_itm.contains(&recipient.clone());373 }418374419 Self::transfer(origin, collection_id, item_id, new_owner)?;375 if !approved376 {377 Self::check_owner_or_admin_permissions(collection_id, sender)?;378 }379 380 let target_collection = <Collection<T>>::get(collection_id);420381382 // TODO: implement other modes383 ensure!(target_collection.mode == CollectionMode::NFT, "Collection type not implemented");384385 if target_collection.mode == CollectionMode::NFT386 {387 Self::transfer_nft(collection_id, item_id, recipient)?;388 }389421 Ok(())390 Ok(())422 }391 }423392424 #[weight = 0]393 #[weight = 0]425 pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {394 pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {426395427 let no_perm_mes = "You do not have permissions to modify this collection";396 // let no_perm_mes = "You do not have permissions to modify this collection";428 ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);397 // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);429 let list_itm = <ApprovedList<T>>::get((collection_id, item_id));398 // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));430 ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);399 // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);431400432 // on_nft_received call401 // // on_nft_received call433402434 Self::transfer(origin, collection_id, item_id, new_owner)?;403 // Self::transfer(origin, collection_id, item_id, new_owner)?;435404436 Ok(())405 Ok(())437 }406 }440409441impl<T: Trait> Module<T> {410impl<T: Trait> Module<T> {411412 fn collection_exists(collection_id: u64) -> DispatchResult{413 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");414 Ok(())415 }416417 fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {418419 Self::collection_exists(collection_id)?;420421 let target_collection = <Collection<T>>::get(collection_id);422 ensure!(subject == target_collection.owner, "You do not own this collection");423424 Ok(())425 }426427 fn check_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {428429 Self::collection_exists(collection_id)?;430431 let target_collection = <Collection<T>>::get(collection_id);432 let is_owner = subject == target_collection.owner;433434 let no_perm_mes = "You do not have permissions to modify this collection";435 let exists = <AdminList<T>>::contains_key(collection_id);436437 if !is_owner438 {439 ensure!(exists, no_perm_mes);440 ensure!(<AdminList<T>>::get(collection_id).contains(&subject), no_perm_mes);441 }442 Ok(())443 }444445 fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool{446447 let mut result = false;448 let target_collection = <Collection<T>>::get(collection_id);449450 if target_collection.mode == CollectionMode::NFT451 {452 let item = <NftItemList<T>>::get(collection_id, item_id);453 result = item.owner == subject;454 }455456 if target_collection.mode == CollectionMode::Fungible457 {458 let item = <FungibleItemList<T>>::get(collection_id, item_id);459 result = item.owner.contains(&subject);460 }461462 if target_collection.mode == CollectionMode::ReFungible463 {464 let item = <ReFungibleItemList<T>>::get(collection_id, item_id);465 result = item.owner.iter().any(|i| i.owner == subject);466 }467468 result469 }470471 fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {472473 let current_index = <ItemListIndex>::get(item.collection)474 .checked_add(1)475 .expect("Item list index id error");476477 Self::add_token_index(item.collection, current_index, item.owner.clone())?;478479 <ItemListIndex>::insert(item.collection, current_index);480 <NftItemList<T>>::insert(item.collection, current_index, item);481482 Ok(())483 }484485 fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {486 487 let item = <NftItemList<T>>::get(collection_id, item_id);488 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;489490 // update balance491 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone()).checked_sub(1).unwrap();492 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);493 <NftItemList<T>>::remove(collection_id, item_id);494495 Ok(())496 }497498 fn transfer_nft(collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {499500 let mut item = <NftItemList<T>>::get(collection_id, item_id);501502 // update balance503 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone()).checked_sub(1).unwrap();504 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);505506 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone()).checked_add(1).unwrap();507 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);508509 // change owner510 let old_owner = item.owner.clone();511 item.owner = new_owner.clone();512 <NftItemList<T>>::insert(collection_id, item_id, item);513514 // update index collection515 Self::move_token_index(collection_id, item_id, old_owner, new_owner.clone())?;516517 // reset approved list518 let itm: Vec<T::AccountId> = Vec::new();519 <ApprovedList<T>>::insert(collection_id, item_id, itm);520521 // remove item522 <NftItemList<T>>::remove(collection_id, item_id);523524 Ok(())525 }526442 fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {527 fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {443 let list_exists = <AddressTokens<T>>::contains_key((collection_id, owner.clone()));528 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());444 if list_exists {529 if list_exists {445 let mut list = <AddressTokens<T>>::get((collection_id, owner.clone()));530 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());446 let item_contains = list.contains(&item_index.clone());531 let item_contains = list.contains(&item_index.clone());447532448 if !item_contains {533 if !item_contains {449 list.push(item_index.clone());534 list.push(item_index.clone());450 }535 }451536452 <AddressTokens<T>>::insert((collection_id, owner.clone()), list);537 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);453 } else {538 } else {454 let mut itm = Vec::new();539 let mut itm = Vec::new();455 itm.push(item_index.clone());540 itm.push(item_index.clone());456 <AddressTokens<T>>::insert((collection_id, owner), itm);541 <AddressTokens<T>>::insert(collection_id, owner, itm);457 }542 }458543459 Ok(())544 Ok(())464 item_index: u64,549 item_index: u64,465 owner: T::AccountId,550 owner: T::AccountId,466 ) -> DispatchResult {551 ) -> DispatchResult {467 let list_exists = <AddressTokens<T>>::contains_key((collection_id, owner.clone()));552 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());468 if list_exists {553 if list_exists {469 let mut list = <AddressTokens<T>>::get((collection_id, owner.clone()));554 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());470 let item_contains = list.contains(&item_index.clone());555 let item_contains = list.contains(&item_index.clone());471556472 if item_contains {557 if item_contains {473 list.retain(|&item| item != item_index);558 list.retain(|&item| item != item_index);474 <AddressTokens<T>>::insert((collection_id, owner), list);559 <AddressTokens<T>>::insert(collection_id, owner, list);475 }560 }476 }561 }477562pallets/nft/src/mock.rsdiffbeforeafterboth1// Creating mock runtime here1// Creating mock runtime here223use crate::{Module, Trait};3use crate::{Module, Trait};4use frame_support::{impl_outer_origin, parameter_types, weights::Weight};5use frame_system as system;4use frame_system as system;6use sp_core::H256;5use sp_core::H256;7use sp_runtime::{6use sp_runtime::{8 testing::Header,7 testing::Header,9 traits::{BlakeTwo256, IdentityLookup},8 traits::{BlakeTwo256, IdentityLookup, Saturating},10 Perbill,9 Perbill,11};10};11use frame_support::{12 parameter_types, impl_outer_origin,13 weights::{14 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight},15 Weight,16 },17};121813impl_outer_origin! {19impl_outer_origin! {14 pub enum Origin for Test {}20 pub enum Origin for Test {}24 pub const MaximumBlockWeight: Weight = 1024;30 pub const MaximumBlockWeight: Weight = 1024;25 pub const MaximumBlockLength: u32 = 2 * 1024;31 pub const MaximumBlockLength: u32 = 2 * 1024;26 pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75);32 pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75);33 pub MaximumExtrinsicWeight: Weight = AvailableBlockRatio::get()34 .saturating_sub(Perbill::from_percent(10)) * MaximumBlockWeight::get();27}35}3628impl system::Trait for Test {37impl system::Trait for Test {40 type MaximumBlockWeight = MaximumBlockWeight;49 type MaximumBlockWeight = MaximumBlockWeight;41 type MaximumBlockLength = MaximumBlockLength;50 type MaximumBlockLength = MaximumBlockLength;42 type AvailableBlockRatio = AvailableBlockRatio;51 type AvailableBlockRatio = AvailableBlockRatio;52 type BaseCallFilter = (); 53 type DbWeight = RocksDbWeight; 54 type BlockExecutionWeight = BlockExecutionWeight; 55 type ExtrinsicBaseWeight = ExtrinsicBaseWeight;56 type MaximumExtrinsicWeight = MaximumExtrinsicWeight;43 type Version = ();57 type Version = ();44 type ModuleToIndex = ();58 type ModuleToIndex = ();45 type AccountData = ();59 type AccountData = ();