--- a/node/src/chain_spec.rs +++ b/node/src/chain_spec.rs @@ -179,9 +179,10 @@ .collect(), }), pallet_nft: Some(NftConfig { - collection: vec![( + collection_id: vec![( 1, - CollectionType { + Collection { + id: 1, owner: get_account_id_from_seed::("Alice"), mode: CollectionMode::NFT, access: AccessMode::Normal, --- a/pallets/nft/src/lib.rs +++ b/pallets/nft/src/lib.rs @@ -125,7 +125,8 @@ #[derive(Encode, Decode, Clone, PartialEq)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -pub struct CollectionType { +pub struct Collection { + pub id: CollectionId, pub owner: T::AccountId, pub mode: CollectionMode, pub access: AccessMode, @@ -426,7 +427,7 @@ pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32; // Basic collections - pub Collection get(fn collection) config(): map hasher(identity) CollectionId => Option> = None; + pub CollectionById get(fn collection_id) config(): map hasher(identity) CollectionId => Option> = None; pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec; pub WhiteList get(fn white_list): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => bool; @@ -464,7 +465,7 @@ add_extra_genesis { build(|config: &GenesisConfig| { // Modification of storage - for (_num, _c) in &config.collection { + for (_num, _c) in &config.collection_id { >::init_collection(_c); } @@ -615,7 +616,8 @@ }; // Create new collection - let new_collection = CollectionType { + let new_collection = Collection { + id: next_id, owner: who.clone(), name: collection_name, mode: mode.clone(), @@ -634,7 +636,7 @@ }; // Add new collection to map - >::insert(next_id, new_collection); + >::insert(next_id, new_collection); // call event Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone())); @@ -655,10 +657,9 @@ pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult { let sender = ensure_signed(origin)?; - Self::check_owner_permissions(collection_id, sender)?; - - let target_collection = >::get(collection_id); - if !target_collection.limits.owner_can_destroy { + let collection = Self::get_collection(collection_id)?; + Self::check_owner_permissions(&collection, sender)?; + if !collection.limits.owner_can_destroy { fail!(Error::::NoPermission); } @@ -667,7 +668,7 @@ >::remove_prefix(collection_id); ::remove(collection_id); >::remove(collection_id); - >::remove(collection_id); + >::remove(collection_id); >::remove_prefix(collection_id); >::remove_prefix(collection_id); @@ -709,7 +710,8 @@ pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{ let sender = ensure_signed(origin)?; - Self::check_owner_or_admin_permissions(collection_id, sender)?; + let collection = Self::get_collection(collection_id)?; + Self::check_owner_or_admin_permissions(&collection, sender)?; >::insert(collection_id, address, true); @@ -732,7 +734,8 @@ pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{ let sender = ensure_signed(origin)?; - Self::check_owner_or_admin_permissions(collection_id, sender)?; + let collection = Self::get_collection(collection_id)?; + Self::check_owner_or_admin_permissions(&collection, sender)?; >::remove(collection_id, address); @@ -755,10 +758,10 @@ { let sender = ensure_signed(origin)?; - Self::check_owner_permissions(collection_id, sender)?; - let mut target_collection = >::get(collection_id).unwrap(); + let mut target_collection = Self::get_collection(collection_id)?; + Self::check_owner_permissions(&target_collection, sender)?; target_collection.access = mode; - >::insert(collection_id, target_collection); + Self::save_collection(target_collection); Ok(()) } @@ -781,10 +784,10 @@ { let sender = ensure_signed(origin)?; - Self::check_owner_permissions(collection_id, sender)?; - let mut target_collection = >::get(collection_id).unwrap(); + let mut target_collection = Self::get_collection(collection_id)?; + Self::check_owner_permissions(&target_collection, sender)?; target_collection.mint_mode = mint_permission; - >::insert(collection_id, target_collection); + Self::save_collection(target_collection); Ok(()) } @@ -804,10 +807,10 @@ pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult { let sender = ensure_signed(origin)?; - Self::check_owner_permissions(collection_id, sender)?; - let mut target_collection = >::get(collection_id).unwrap(); + let mut target_collection = Self::get_collection(collection_id)?; + Self::check_owner_permissions(&target_collection, sender)?; target_collection.owner = new_owner; - >::insert(collection_id, target_collection); + Self::save_collection(target_collection); Ok(()) } @@ -829,7 +832,8 @@ pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult { let sender = ensure_signed(origin)?; - Self::check_owner_or_admin_permissions(collection_id, sender)?; + let collection = Self::get_collection(collection_id)?; + Self::check_owner_or_admin_permissions(&collection, sender)?; let mut admin_arr: Vec = Vec::new(); if >::contains_key(collection_id) @@ -863,7 +867,8 @@ pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult { let sender = ensure_signed(origin)?; - Self::check_owner_or_admin_permissions(collection_id, sender)?; + let collection = Self::get_collection(collection_id)?; + Self::check_owner_or_admin_permissions(&collection, sender)?; ensure!(>::contains_key(collection_id), Error::::AdminNotFound); let mut admin_arr = >::get(collection_id); @@ -886,13 +891,12 @@ pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult { let sender = ensure_signed(origin)?; - - let mut target_collection = >::get(collection_id).ok_or(Error::::CollectionNotFound)?; - ensure!(sender == target_collection.owner, Error::::NoPermission); + let mut target_collection = Self::get_collection(collection_id)?; + Self::check_owner_permissions(&target_collection, sender)?; target_collection.sponsor = new_sponsor; target_collection.sponsor_confirmed = false; - >::insert(collection_id, target_collection); + Self::save_collection(target_collection); Ok(()) } @@ -909,11 +913,11 @@ let sender = ensure_signed(origin)?; - let mut target_collection = >::get(collection_id).ok_or(Error::::CollectionNotFound)?; + let mut target_collection = Self::get_collection(collection_id)?; ensure!(sender == target_collection.sponsor, Error::::ConfirmUnsetSponsorFail); target_collection.sponsor_confirmed = true; - >::insert(collection_id, target_collection); + Self::save_collection(target_collection); Ok(()) } @@ -932,12 +936,12 @@ let sender = ensure_signed(origin)?; - let mut target_collection = >::get(collection_id).ok_or(Error::::CollectionNotFound)?; - ensure!(sender == target_collection.owner, Error::::NoPermission); + let mut target_collection = Self::get_collection(collection_id)?; + Self::check_owner_permissions(&target_collection, sender)?; target_collection.sponsor = T::AccountId::default(); target_collection.sponsor_confirmed = false; - >::insert(collection_id, target_collection); + Self::save_collection(target_collection); Ok(()) } @@ -971,13 +975,11 @@ let sender = ensure_signed(origin)?; - Self::collection_exists(collection_id)?; + let target_collection = Self::get_collection(collection_id)?; - let target_collection = >::get(collection_id).unwrap(); - - Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?; + Self::can_create_items_in_collection(&target_collection, &sender, &owner)?; Self::validate_create_item_args(&target_collection, &data)?; - Self::create_item_no_validation(collection_id, owner, data)?; + Self::create_item_no_validation(&target_collection, owner, data)?; Ok(()) } @@ -1008,16 +1010,15 @@ ensure!(items_data.len() > 0, Error::::EmptyArgument); let sender = ensure_signed(origin)?; - Self::collection_exists(collection_id)?; - let target_collection = >::get(collection_id).unwrap(); + let target_collection = Self::get_collection(collection_id)?; - Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?; + Self::can_create_items_in_collection(&target_collection, &sender, &owner)?; for data in &items_data { Self::validate_create_item_args(&target_collection, data)?; } for data in &items_data { - Self::create_item_no_validation(collection_id, owner.clone(), data.clone())?; + Self::create_item_no_validation(&target_collection, owner.clone(), data.clone())?; } Ok(()) @@ -1040,33 +1041,32 @@ pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult { let sender = ensure_signed(origin)?; - Self::collection_exists(collection_id)?; // Transfer permissions check - let target_collection = >::get(collection_id).unwrap(); + let target_collection = Self::get_collection(collection_id)?; ensure!( - Self::is_item_owner(sender.clone(), collection_id, item_id) || + Self::is_item_owner(sender.clone(), &target_collection, item_id) || ( target_collection.limits.owner_can_transfer && - Self::is_owner_or_admin_permissions(collection_id, sender.clone()) + Self::is_owner_or_admin_permissions(&target_collection, sender.clone()) ), Error::::NoPermission ); if target_collection.access == AccessMode::WhiteList { - Self::check_white_list(collection_id, &sender)?; + Self::check_white_list(&target_collection, &sender)?; } match target_collection.mode { - CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?, - CollectionMode::Fungible(_) => Self::burn_fungible_item(&sender, collection_id, value)?, - CollectionMode::ReFungible => Self::burn_refungible_item(collection_id, item_id, &sender)?, + CollectionMode::NFT => Self::burn_nft_item(&target_collection, item_id)?, + CollectionMode::Fungible(_) => Self::burn_fungible_item(&sender, &target_collection, value)?, + CollectionMode::ReFungible => Self::burn_refungible_item(&target_collection, item_id, &sender)?, _ => () }; // call event - Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id)); + Self::deposit_event(RawEvent::ItemDestroyed(target_collection.id, item_id)); Ok(()) } @@ -1097,7 +1097,9 @@ #[weight = ::WeightInfo::transfer()] pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult { let sender = ensure_signed(origin)?; - Self::transfer_internal(sender, recipient, collection_id, item_id, value) + let collection = Self::get_collection(collection_id)?; + + Self::transfer_internal(sender, recipient, &collection, item_id, value) } /// Set, change, or remove approved address to transfer the ownership of the NFT. @@ -1119,23 +1121,22 @@ pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult { let sender = ensure_signed(origin)?; + let target_collection = Self::get_collection(collection_id)?; - Self::collection_exists(collection_id)?; - Self::token_exists(collection_id, item_id, &sender)?; + Self::token_exists(&target_collection, item_id, &sender)?; // Transfer permissions check - let target_collection = >::get(collection_id).unwrap(); let allowance_limit = if ( target_collection.limits.owner_can_transfer && Self::is_owner_or_admin_permissions( - collection_id, + &target_collection, sender.clone(), ) ) { None } else if let Some(amount) = Self::owned_amount( sender.clone(), - collection_id, + &target_collection, item_id, ) { Some(amount) @@ -1144,8 +1145,8 @@ }; if target_collection.access == AccessMode::WhiteList { - Self::check_white_list(collection_id, &sender)?; - Self::check_white_list(collection_id, &spender)?; + Self::check_white_list(&target_collection, &sender)?; + Self::check_white_list(&target_collection, &spender)?; } let allowance_exists = >::contains_key(collection_id, (item_id, &sender, &spender)); @@ -1184,6 +1185,8 @@ pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult { let sender = ensure_signed(origin)?; + let target_collection = Self::get_collection(collection_id)?; + let mut appoved_transfer = false; // Check approval @@ -1193,25 +1196,23 @@ ensure!(approval >= value, Error::::TokenValueNotEnough); appoved_transfer = true; } - - let target_collection = >::get(collection_id).unwrap(); // Limits check - Self::is_correct_transfer(collection_id, &target_collection, &recipient)?; + Self::is_correct_transfer(&target_collection, &recipient)?; // Transfer permissions check ensure!( appoved_transfer || ( target_collection.limits.owner_can_transfer && - Self::is_owner_or_admin_permissions(collection_id, sender.clone()) + Self::is_owner_or_admin_permissions(&target_collection, sender.clone()) ), Error::::NoPermission ); if target_collection.access == AccessMode::WhiteList { - Self::check_white_list(collection_id, &sender)?; - Self::check_white_list(collection_id, &recipient)?; + Self::check_white_list(&target_collection, &sender)?; + Self::check_white_list(&target_collection, &recipient)?; } // Reduce approval by transferred amount or remove if remaining approval drops to 0 @@ -1224,9 +1225,9 @@ match target_collection.mode { - CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?, - CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, value, &from, &recipient)?, - CollectionMode::ReFungible => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?, + CollectionMode::NFT => Self::transfer_nft(&target_collection, item_id, from, recipient)?, + CollectionMode::Fungible(_) => Self::transfer_fungible(&target_collection, value, &from, &recipient)?, + CollectionMode::ReFungible => Self::transfer_refungible(&target_collection, item_id, value, from.clone(), recipient)?, _ => () }; @@ -1269,21 +1270,20 @@ ) -> DispatchResult { let sender = ensure_signed(origin)?; - Self::collection_exists(collection_id)?; - Self::token_exists(collection_id, item_id, &sender)?; + let target_collection = Self::get_collection(collection_id)?; + Self::token_exists(&target_collection, item_id, &sender)?; ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::::TokenVariableDataLimitExceeded); // Modify permissions check - let target_collection = >::get(collection_id).unwrap(); - ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) || - Self::is_owner_or_admin_permissions(collection_id, sender.clone()), + ensure!(Self::is_item_owner(sender.clone(), &target_collection, item_id) || + Self::is_owner_or_admin_permissions(&target_collection, sender.clone()), Error::::NoPermission); match target_collection.mode { - CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?, - CollectionMode::ReFungible => Self::set_re_fungible_variable_data(collection_id, item_id, data)?, + CollectionMode::NFT => Self::set_nft_variable_data(&target_collection, item_id, data)?, + CollectionMode::ReFungible => Self::set_re_fungible_variable_data(&target_collection, item_id, data)?, CollectionMode::Fungible(_) => fail!(Error::::CantStoreMetadataInFungibleTokens), _ => fail!(Error::::UnexpectedCollectionType) }; @@ -1312,10 +1312,10 @@ version: SchemaVersion ) -> DispatchResult { let sender = ensure_signed(origin)?; - Self::check_owner_or_admin_permissions(collection_id, sender.clone())?; - let mut target_collection = >::get(collection_id).unwrap(); + let mut target_collection = Self::get_collection(collection_id)?; + Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?; target_collection.schema_version = version; - >::insert(collection_id, target_collection); + Self::save_collection(target_collection); Ok(()) } @@ -1339,14 +1339,14 @@ schema: Vec ) -> DispatchResult { let sender = ensure_signed(origin)?; - Self::check_owner_or_admin_permissions(collection_id, sender.clone())?; + let mut target_collection = Self::get_collection(collection_id)?; + Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?; // check schema limit ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, ""); - let mut target_collection = >::get(collection_id).unwrap(); target_collection.offchain_schema = schema; - >::insert(collection_id, target_collection); + Self::save_collection(target_collection); Ok(()) } @@ -1370,14 +1370,14 @@ schema: Vec ) -> DispatchResult { let sender = ensure_signed(origin)?; - Self::check_owner_or_admin_permissions(collection_id, sender.clone())?; + let mut target_collection = Self::get_collection(collection_id)?; + Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?; // check schema limit ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, ""); - let mut target_collection = >::get(collection_id).unwrap(); target_collection.const_on_chain_schema = schema; - >::insert(collection_id, target_collection); + Self::save_collection(target_collection); Ok(()) } @@ -1401,14 +1401,14 @@ schema: Vec ) -> DispatchResult { let sender = ensure_signed(origin)?; - Self::check_owner_or_admin_permissions(collection_id, sender.clone())?; + let mut target_collection = Self::get_collection(collection_id)?; + Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?; // check schema limit ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, ""); - let mut target_collection = >::get(collection_id).unwrap(); target_collection.variable_on_chain_schema = schema; - >::insert(collection_id, target_collection); + Self::save_collection(target_collection); Ok(()) } @@ -1577,8 +1577,8 @@ new_limits: CollectionLimits, ) -> DispatchResult { let sender = ensure_signed(origin)?; - Self::check_owner_permissions(collection_id, sender.clone())?; - let mut target_collection = >::get(collection_id).unwrap(); + let mut target_collection = Self::get_collection(collection_id)?; + Self::check_owner_permissions(&target_collection, sender.clone())?; let old_limits = target_collection.limits; let chain_limits = ChainLimit::get(); @@ -1599,7 +1599,7 @@ ); target_collection.limits = new_limits; - >::insert(collection_id, target_collection); + Self::save_collection(target_collection); Ok(()) } @@ -1607,39 +1607,37 @@ } impl Module { - - pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult { - let target_collection = >::get(collection_id).unwrap(); - + pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, target_collection: &Collection, item_id: TokenId, value: u128) -> DispatchResult { // Limits check - Self::is_correct_transfer(collection_id, &target_collection, &recipient)?; + Self::is_correct_transfer(target_collection, &recipient)?; // Transfer permissions check - ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) || - Self::is_owner_or_admin_permissions(collection_id, sender.clone()), + ensure!(Self::is_item_owner(sender.clone(), target_collection, item_id) || + Self::is_owner_or_admin_permissions(target_collection, sender.clone()), Error::::NoPermission); if target_collection.access == AccessMode::WhiteList { - Self::check_white_list(collection_id, &sender)?; - Self::check_white_list(collection_id, &recipient)?; + Self::check_white_list(target_collection, &sender)?; + Self::check_white_list(target_collection, &recipient)?; } match target_collection.mode { - CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient.clone())?, - CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, value, &sender, &recipient)?, - CollectionMode::ReFungible => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient.clone())?, + CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?, + CollectionMode::Fungible(_) => Self::transfer_fungible(target_collection, value, &sender, &recipient)?, + CollectionMode::ReFungible => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?, _ => () }; - Self::deposit_event(RawEvent::Transfer(collection_id, item_id, sender, recipient, value)); + Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender, recipient, value)); Ok(()) } - fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType, recipient: &T::AccountId) -> DispatchResult { + fn is_correct_transfer(collection: &Collection, recipient: &T::AccountId) -> DispatchResult { + let collection_id = collection.id; // check token limit and account token limit let account_items: u32 = >::get(collection_id, recipient).len() as u32; @@ -1648,7 +1646,8 @@ Ok(()) } - fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult { + fn can_create_items_in_collection(collection: &Collection, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult { + let collection_id = collection.id; // check token limit and account token limit let total_items: u32 = ItemListIndex::get(collection_id); @@ -1656,16 +1655,16 @@ ensure!(collection.limits.token_limit > total_items, Error::::CollectionTokenLimitExceeded); ensure!(collection.limits.account_token_ownership_limit > account_items, Error::::AccountTokenLimitExceeded); - if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) { + if !Self::is_owner_or_admin_permissions(collection, sender.clone()) { ensure!(collection.mint_mode == true, Error::::PublicMintingNotAllowed); - Self::check_white_list(collection_id, owner)?; - Self::check_white_list(collection_id, sender)?; + Self::check_white_list(collection, owner)?; + Self::check_white_list(collection, sender)?; } Ok(()) } - fn validate_create_item_args(target_collection: &CollectionType, data: &CreateItemData) -> DispatchResult { + fn validate_create_item_args(target_collection: &Collection, data: &CreateItemData) -> DispatchResult { match target_collection.mode { CollectionMode::NFT => { @@ -1703,7 +1702,9 @@ Ok(()) } - fn create_item_no_validation(collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult { + fn create_item_no_validation(collection: &Collection, owner: T::AccountId, data: CreateItemData) -> DispatchResult { + let collection_id = collection.id; + match data { CreateItemData::NFT(data) => { @@ -1713,10 +1714,10 @@ variable_data: data.variable_data }; - Self::add_nft_item(collection_id, item)?; + Self::add_nft_item(collection, item)?; }, CreateItemData::Fungible(data) => { - Self::add_fungible_item(collection_id, &owner, data.value)?; + Self::add_fungible_item(collection, &owner, data.value)?; }, CreateItemData::ReFungible(data) => { let mut owner_list = Vec::new(); @@ -1728,7 +1729,7 @@ variable_data: data.variable_data }; - Self::add_refungible_item(collection_id, item)?; + Self::add_refungible_item(collection, item)?; } }; @@ -1738,7 +1739,8 @@ Ok(()) } - fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult { + fn add_fungible_item(collection: &Collection, owner: &T::AccountId, value: u128) -> DispatchResult { + let collection_id = collection.id; // Does new owner already have an account? let mut balance: u128 = 0; @@ -1761,7 +1763,9 @@ Ok(()) } - fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType) -> DispatchResult { + fn add_refungible_item(collection: &Collection, item: ReFungibleItemType) -> DispatchResult { + let collection_id = collection.id; + let current_index = ::get(collection_id) .checked_add(1) .ok_or(Error::::NumOverflow)?; @@ -1784,7 +1788,9 @@ Ok(()) } - fn add_nft_item(collection_id: CollectionId, item: NftItemType) -> DispatchResult { + fn add_nft_item(collection: &Collection, item: NftItemType) -> DispatchResult { + let collection_id = collection.id; + let current_index = ::get(collection_id) .checked_add(1) .ok_or(Error::::NumOverflow)?; @@ -1805,10 +1811,12 @@ } fn burn_refungible_item( - collection_id: CollectionId, + collection: &Collection, item_id: TokenId, owner: &T::AccountId, ) -> DispatchResult { + let collection_id = collection.id; + ensure!( >::contains_key(collection_id, item_id), Error::::TokenNotFound @@ -1849,7 +1857,9 @@ Ok(()) } - fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult { + fn burn_nft_item(collection: &Collection, item_id: TokenId) -> DispatchResult { + let collection_id = collection.id; + ensure!( >::contains_key(collection_id, item_id), Error::::TokenNotFound @@ -1868,7 +1878,9 @@ Ok(()) } - fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult { + fn burn_fungible_item(owner: &T::AccountId, collection: &Collection, value: u128) -> DispatchResult { + let collection_id = collection.id; + ensure!( >::contains_key(collection_id, owner), Error::::TokenNotFound @@ -1893,18 +1905,16 @@ Ok(()) } - fn collection_exists(collection_id: CollectionId) -> DispatchResult { - ensure!( - >::contains_key(collection_id), - Error::::CollectionNotFound - ); - Ok(()) + pub fn get_collection(collection_id: CollectionId) -> Result, sp_runtime::DispatchError> { + Ok(>::get(collection_id) + .ok_or(Error::::CollectionNotFound)?) } - fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult { - Self::collection_exists(collection_id)?; + fn save_collection(collection: Collection) { + >::insert(collection.id, collection); + } - let target_collection = >::get(collection_id).unwrap(); + fn check_owner_permissions(target_collection: &Collection, subject: T::AccountId) -> DispatchResult { ensure!( subject == target_collection.owner, Error::::NoPermission @@ -1913,13 +1923,12 @@ Ok(()) } - fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool { - let target_collection = >::get(collection_id).unwrap(); - let mut result: bool = subject == target_collection.owner; - let exists = >::contains_key(collection_id); + fn is_owner_or_admin_permissions(collection: &Collection, subject: T::AccountId) -> bool { + let mut result: bool = subject == collection.owner; + let exists = >::contains_key(collection.id); if !result & exists { - if >::get(collection_id).contains(&subject) { + if >::get(collection.id).contains(&subject) { result = true } } @@ -1928,11 +1937,10 @@ } fn check_owner_or_admin_permissions( - collection_id: CollectionId, + collection: &Collection, subject: T::AccountId, ) -> DispatchResult { - Self::collection_exists(collection_id)?; - let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone()); + let result = Self::is_owner_or_admin_permissions(collection, subject.clone()); ensure!( result, @@ -1943,10 +1951,10 @@ fn owned_amount( subject: T::AccountId, - collection_id: CollectionId, + target_collection: &Collection, item_id: TokenId, ) -> Option { - let target_collection = >::get(collection_id); + let collection_id = target_collection.id; match target_collection.mode { CollectionMode::NFT => { @@ -1971,8 +1979,8 @@ } } - fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool { - let target_collection = >::get(collection_id).unwrap(); + fn is_item_owner(subject: T::AccountId, target_collection: &Collection, item_id: TokenId) -> bool { + let collection_id = target_collection.id; match target_collection.mode { CollectionMode::NFT => { @@ -1991,7 +1999,9 @@ } } - fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult { + fn check_white_list(collection: &Collection, address: &T::AccountId) -> DispatchResult { + let collection_id = collection.id; + let mes = Error::::AddresNotInWhiteList; ensure!(>::contains_key(collection_id, address), mes); @@ -2001,11 +2011,11 @@ /// Check if token exists. In case of Fungible, check if there is an entry for /// the owner in fungible balances double map fn token_exists( - collection_id: CollectionId, + target_collection: &Collection, item_id: TokenId, owner: &T::AccountId ) -> DispatchResult { - let target_collection = >::get(collection_id).unwrap(); + let collection_id = target_collection.id; let exists = match target_collection.mode { CollectionMode::NFT => >::contains_key(collection_id, item_id), @@ -2019,18 +2029,19 @@ } fn transfer_fungible( - collection_id: CollectionId, + collection: &Collection, value: u128, owner: &T::AccountId, recipient: &T::AccountId, ) -> DispatchResult { - Self::token_exists(collection_id, 0, owner)?; + let collection_id = collection.id; + Self::token_exists(&collection, 0, owner)?; let mut balance = >::get(collection_id, owner); ensure!(balance.value >= value, Error::::TokenValueTooLow); // Send balance to recipient (updates balanceOf of recipient) - Self::add_fungible_item(collection_id, recipient, value)?; + Self::add_fungible_item(collection, recipient, value)?; // update balanceOf of sender >::insert(collection_id, (*owner).clone(), balance.value - value); @@ -2048,13 +2059,14 @@ } fn transfer_refungible( - collection_id: CollectionId, + collection: &Collection, item_id: TokenId, value: u128, owner: T::AccountId, new_owner: T::AccountId, ) -> DispatchResult { - Self::token_exists(collection_id, item_id, &owner)?; + let collection_id = collection.id; + Self::token_exists(collection, item_id, &owner)?; let full_item = >::get(collection_id, item_id); let item = full_item @@ -2130,12 +2142,13 @@ } fn transfer_nft( - collection_id: CollectionId, + collection: &Collection, item_id: TokenId, sender: T::AccountId, new_owner: T::AccountId, ) -> DispatchResult { - Self::token_exists(collection_id, item_id, &sender)?; + let collection_id = collection.id; + Self::token_exists(&collection, item_id, &sender)?; let mut item = >::get(collection_id, item_id); @@ -2167,10 +2180,11 @@ } fn set_re_fungible_variable_data( - collection_id: CollectionId, + collection: &Collection, item_id: TokenId, data: Vec ) -> DispatchResult { + let collection_id = collection.id; let mut item = >::get(collection_id, item_id); item.variable_data = data; @@ -2181,10 +2195,11 @@ } fn set_nft_variable_data( - collection_id: CollectionId, + collection: &Collection, item_id: TokenId, data: Vec ) -> DispatchResult { + let collection_id = collection.id; let mut item = >::get(collection_id, item_id); item.variable_data = data; @@ -2194,7 +2209,7 @@ Ok(()) } - fn init_collection(item: &CollectionType) { + fn init_collection(item: &Collection) { // check params assert!( item.decimal_points <= MAX_DECIMAL_POINTS, @@ -2274,7 +2289,6 @@ } fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult { - // add to account limit if >::contains_key(owner) { @@ -2449,38 +2463,34 @@ // Determine who is paying transaction fee based on ecnomic model // Parse call to extract collection ID and access collection sponsor - let mut sponsor: T::AccountId = match IsSubType::>::is_sub_type(call) { + let mut sponsor: Option = (|| match IsSubType::>::is_sub_type(call) { Some(Call::create_item(collection_id, _owner, _properties)) => { - let collection = >::get(collection_id).unwrap(); + let collection = >::get(collection_id)?; // sponsor timeout let block_number = >::block_number() as T::BlockNumber; let limit = collection.limits.sponsor_transfer_timeout; - let mut sponsored = true; if >::contains_key((collection_id, &who)) { let last_tx_block = >::get((collection_id, &who)); let limit_time = last_tx_block + limit.into(); if block_number <= limit_time { - sponsored = false; + return None; } - } - if sponsored { - >::insert((collection_id, who.clone()), block_number); } + >::insert((collection_id, who.clone()), block_number); // check free create limit if (collection.limits.sponsored_data_size >= (_properties.len() as u32)) && - (collection.sponsor_confirmed) && - (sponsored) + (collection.sponsor_confirmed) { - collection.sponsor + Some(collection.sponsor) } else { - T::AccountId::default() + None } } Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => { - let collection = >::get(collection_id).unwrap(); + let collection = >::get(collection_id)?; let mut sponsor_transfer = false; if collection.sponsor_confirmed { @@ -2568,16 +2578,16 @@ } if !sponsor_transfer { - T::AccountId::default() + None } else { - collection.sponsor + Some(collection.sponsor) } } Some(Call::set_variable_meta_data(collection_id, item_id, data)) => { let mut sponsor_metadata_changes = false; - let collection = >::get(collection_id).unwrap(); + let collection = >::get(collection_id)?; if collection.sponsor_confirmed && @@ -2600,17 +2610,35 @@ } if !sponsor_metadata_changes { - T::AccountId::default() + None } else { - collection.sponsor + Some(collection.sponsor) } } - _ => T::AccountId::default(), - }; + _ => None, + })(); + + match IsSubType::>::is_sub_type(call) { + Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => { + let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default()); + + let owned_contract = >::contains_key(called_contract.clone()) + && >::get(called_contract.clone()) == *who; + let white_list_enabled = >::contains_key(called_contract.clone()) && >::get(called_contract.clone()); + + if !owned_contract && white_list_enabled { + if !>::contains_key(called_contract.clone(), who) { + return Err(InvalidTransaction::Call.into()); + } + } + }, + _ => {}, + } + // Sponsor smart contracts - sponsor = match IsSubType::>::is_sub_type(call) { + sponsor = sponsor.or_else(|| match IsSubType::>::is_sub_type(call) { // On instantiation: set the contract owner Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => { @@ -2622,7 +2650,7 @@ ); >::insert(new_contract_address.clone(), who.clone()); - T::AccountId::default() + None }, // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is @@ -2630,16 +2658,6 @@ let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default()); - let owned_contract = >::contains_key(called_contract.clone()) - && >::get(called_contract.clone()) == *who; - let white_list_enabled = >::contains_key(called_contract.clone()) && >::get(called_contract.clone()); - - if !owned_contract && white_list_enabled { - if !>::contains_key(called_contract.clone(), who) { - return Err(InvalidTransaction::Call.into()); - } - } - let mut sponsor_transfer = false; if >::contains_key(called_contract.clone()) { let last_tx_block = >::get((&called_contract, &who)); @@ -2655,26 +2673,21 @@ sponsor_transfer = false; } - - let mut sp = T::AccountId::default(); if sponsor_transfer { if >::contains_key(called_contract.clone()) { if >::get(called_contract.clone()) { - sp = called_contract; + return Some(called_contract); } } } - sp + None }, - _ => sponsor, - }; + _ => None, + }); - let mut who_pays_fee: T::AccountId = sponsor.clone(); - if sponsor == T::AccountId::default() { - who_pays_fee = who.clone(); - } + let who_pays_fee = sponsor.unwrap_or_else(|| who.clone()); <::OnChargeTransaction as transaction_payment::OnChargeTransaction>::withdraw_fee(&who_pays_fee, call, info, fee, tip) .map(|i| (fee, i)) --- a/runtime/src/chain_extension.rs +++ b/runtime/src/chain_extension.rs @@ -60,7 +60,9 @@ } let recipient = AccountId32::from(bytes_rec); - match pallet_nft::Module::::transfer_internal(sender, recipient, input.collection_id, input.token_id, input.amount) { + let collection = pallet_nft::Module::::get_collection(input.collection_id)?; + + match pallet_nft::Module::::transfer_internal(sender, recipient, &collection, input.token_id, input.amount) { Ok(_) => Ok(RetVal::Converging(func_id)), _ => Err(DispatchError::Other("Transfer error")) } --- a/runtime_types.json +++ b/runtime_types.json @@ -31,7 +31,8 @@ "ConstData": "Vec", "VariableData": "Vec" }, - "CollectionType": { + "Collection": { + "Id": "CollectionId", "Owner": "AccountId", "Mode": "CollectionMode", "Access": "AccessMode",