difftreelog
fix use OptionQuery for TokenProperties
in: master
9 files changed
pallets/balances-adapter/src/common.rsdiffbeforeafterboth--- a/pallets/balances-adapter/src/common.rs
+++ b/pallets/balances-adapter/src/common.rs
@@ -172,18 +172,16 @@
fail!(<pallet_common::Error<T>>::UnsupportedOperation);
}
- fn get_token_properties_map(&self, _token_id: TokenId) -> up_data_structs::TokenProperties {
+ fn get_token_properties_raw(
+ &self,
+ _token_id: TokenId,
+ ) -> Option<up_data_structs::TokenProperties> {
// No token properties are defined on fungibles
- up_data_structs::TokenProperties::new()
+ None
}
- fn set_token_properties_map(&self, _token_id: TokenId, _map: up_data_structs::TokenProperties) {
- // No token properties are defined on fungibles
- }
-
- fn properties_exist(&self, _token: TokenId) -> bool {
+ fn set_token_properties_raw(&self, _token_id: TokenId, _map: up_data_structs::TokenProperties) {
// No token properties are defined on fungibles
- false
}
fn set_token_property_permissions(
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -2098,18 +2098,13 @@
/// Get token properties raw map.
///
/// * `token_id` - The token which properties are needed.
- fn get_token_properties_map(&self, token_id: TokenId) -> TokenProperties;
+ fn get_token_properties_raw(&self, token_id: TokenId) -> Option<TokenProperties>;
/// Set token properties raw map.
///
/// * `token_id` - The token for which the properties are being set.
/// * `map` - The raw map containing the token's properties.
- fn set_token_properties_map(&self, token_id: TokenId, map: TokenProperties);
-
- /// Whether the given token has properties.
- ///
- /// * `token_id` - The token in question.
- fn properties_exist(&self, token: TokenId) -> bool;
+ fn set_token_properties_raw(&self, token_id: TokenId, map: TokenProperties);
/// Set token property permissions.
///
@@ -2590,7 +2585,7 @@
<PalletEvm<T>>::deposit_log(log);
self.collection
- .set_token_properties_map(token_id, stored_properties.into_inner());
+ .set_token_properties_raw(token_id, stored_properties.into_inner());
}
Ok(())
@@ -2624,7 +2619,7 @@
true
},
get_properties: |token_id| {
- debug_assert!(!collection.properties_exist(token_id));
+ debug_assert!(collection.get_token_properties_raw(token_id).is_none());
TokenProperties::new()
},
_phantom: PhantomData,
@@ -2686,7 +2681,11 @@
is_collection_admin: LazyValue::new(|| collection.is_owner_or_admin(sender)),
property_permissions: LazyValue::new(|| <Pallet<T>>::property_permissions(collection.id)),
check_token_exist: |token_id| collection.token_exists(token_id),
- get_properties: |token_id| collection.get_token_properties_map(token_id),
+ get_properties: |token_id| {
+ collection
+ .get_token_properties_raw(token_id)
+ .unwrap_or_default()
+ },
_phantom: PhantomData,
}
}
pallets/fungible/src/common.rsdiffbeforeafterboth--- a/pallets/fungible/src/common.rs
+++ b/pallets/fungible/src/common.rs
@@ -364,18 +364,16 @@
fail!(<Error<T>>::SettingPropertiesNotAllowed)
}
- fn get_token_properties_map(&self, _token_id: TokenId) -> up_data_structs::TokenProperties {
+ fn get_token_properties_raw(
+ &self,
+ _token_id: TokenId,
+ ) -> Option<up_data_structs::TokenProperties> {
// No token properties are defined on fungibles
- up_data_structs::TokenProperties::new()
+ None
}
- fn set_token_properties_map(&self, _token_id: TokenId, _map: up_data_structs::TokenProperties) {
- // No token properties are defined on fungibles
- }
-
- fn properties_exist(&self, _token: TokenId) -> bool {
+ fn set_token_properties_raw(&self, _token_id: TokenId, _map: up_data_structs::TokenProperties) {
// No token properties are defined on fungibles
- false
}
fn check_nesting(
pallets/nonfungible/src/common.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/common.rs
+++ b/pallets/nonfungible/src/common.rs
@@ -265,12 +265,15 @@
)
}
- fn get_token_properties_map(&self, token_id: TokenId) -> up_data_structs::TokenProperties {
+ fn get_token_properties_raw(
+ &self,
+ token_id: TokenId,
+ ) -> Option<up_data_structs::TokenProperties> {
<TokenProperties<T>>::get((self.id, token_id))
}
- fn set_token_properties_map(&self, token_id: TokenId, map: up_data_structs::TokenProperties) {
- <TokenProperties<T>>::set((self.id, token_id), map)
+ fn set_token_properties_raw(&self, token_id: TokenId, map: up_data_structs::TokenProperties) {
+ <TokenProperties<T>>::insert((self.id, token_id), map)
}
fn set_token_property_permissions(
@@ -287,10 +290,6 @@
)
}
- fn properties_exist(&self, token: TokenId) -> bool {
- <TokenProperties<T>>::contains_key((self.id, token))
- }
-
fn burn_item(
&self,
sender: T::CrossAccountId,
@@ -482,13 +481,15 @@
}
fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue> {
- <Pallet<T>>::token_properties((self.id, token_id))
+ <Pallet<T>>::token_properties((self.id, token_id))?
.get(key)
.cloned()
}
fn token_properties(&self, token_id: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property> {
- let properties = <Pallet<T>>::token_properties((self.id, token_id));
+ let Some(properties) = <Pallet<T>>::token_properties((self.id, token_id)) else {
+ return vec![];
+ };
keys.map(|keys| {
keys.into_iter()
pallets/nonfungible/src/erc.rsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! # Nonfungible Pallet EVM API18//!19//! Provides ERC-721 standart support implementation and EVM API for unique extensions for Nonfungible Pallet.20//! Method implementations are mostly doing parameter conversion and calling Nonfungible Pallet methods.2122extern crate alloc;2324use alloc::string::ToString;25use core::{26 char::{REPLACEMENT_CHARACTER, decode_utf16},27 convert::TryInto,28};29use evm_coder::{abi::AbiType, AbiCoder, ToLog, generate_stubgen, solidity_interface, types::*};30use frame_support::BoundedVec;31use up_data_structs::{32 TokenId, PropertyPermission, PropertyKeyPermission, Property, CollectionId, PropertyKey,33 CollectionPropertiesVec,34};35use pallet_evm_coder_substrate::{36 dispatch_to_evm, frontier_contract,37 execution::{Result, PreDispatch, Error},38};39use sp_std::{vec::Vec, vec};40use pallet_common::{41 CollectionHandle, CollectionPropertyPermissions, CommonCollectionOperations,42 erc::{CommonEvmHandler, PrecompileResult, CollectionCall, static_property::key},43 eth::{self, TokenUri},44 CommonWeightInfo,45};46use pallet_evm::{account::CrossAccountId, PrecompileHandle};47use pallet_evm_coder_substrate::call;48use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};49use sp_core::{U256, Get};5051use crate::{52 AccountBalance, Config, CreateItemData, NonfungibleHandle, Pallet, TokenData, TokensMinted,53 TokenProperties, SelfWeightOf, weights::WeightInfo, common::CommonWeights,54};5556/// Nft events.57#[derive(ToLog)]58pub enum ERC721TokenEvent {59 /// The token has been changed.60 TokenChanged {61 /// Token ID.62 #[indexed]63 token_id: U256,64 },65}6667/// Token minting parameters68#[derive(AbiCoder, Default, Debug)]69pub struct MintTokenData {70 /// Minted token owner71 pub owner: eth::CrossAddress,72 /// Minted token properties73 pub properties: Vec<eth::Property>,74}7576frontier_contract! {77 macro_rules! NonfungibleHandle_result {...}78 impl<T: Config> Contract for NonfungibleHandle<T> {...}79}8081/// @title A contract that allows to set and delete token properties and change token property permissions.82#[solidity_interface(name = TokenProperties, events(ERC721TokenEvent), enum(derive(PreDispatch)), enum_attr(weight))]83impl<T: Config> NonfungibleHandle<T> {84 /// @notice Set permissions for token property.85 /// @dev Throws error if `msg.sender` is not admin or owner of the collection.86 /// @param key Property key.87 /// @param isMutable Permission to mutate property.88 /// @param collectionAdmin Permission to mutate property by collection admin if property is mutable.89 /// @param tokenOwner Permission to mutate property by token owner if property is mutable.90 #[solidity(hide)]91 #[weight(<SelfWeightOf<T>>::set_token_property_permissions(1))]92 fn set_token_property_permission(93 &mut self,94 caller: Caller,95 key: String,96 is_mutable: bool,97 collection_admin: bool,98 token_owner: bool,99 ) -> Result<()> {100 let caller = T::CrossAccountId::from_eth(caller);101 <Pallet<T>>::set_token_property_permissions(102 self,103 &caller,104 vec![PropertyKeyPermission {105 key: <Vec<u8>>::from(key)106 .try_into()107 .map_err(|_| "too long key")?,108 permission: PropertyPermission {109 mutable: is_mutable,110 collection_admin,111 token_owner,112 },113 }],114 )115 .map_err(dispatch_to_evm::<T>)116 }117118 /// @notice Set permissions for token property.119 /// @dev Throws error if `msg.sender` is not admin or owner of the collection.120 /// @param permissions Permissions for keys.121 #[weight(<SelfWeightOf<T>>::set_token_property_permissions(permissions.len() as u32))]122 fn set_token_property_permissions(123 &mut self,124 caller: Caller,125 permissions: Vec<eth::TokenPropertyPermission>,126 ) -> Result<()> {127 let caller = T::CrossAccountId::from_eth(caller);128 let perms = eth::TokenPropertyPermission::into_property_key_permissions(permissions)?;129130 <Pallet<T>>::set_token_property_permissions(self, &caller, perms)131 .map_err(dispatch_to_evm::<T>)132 }133134 /// @notice Get permissions for token properties.135 fn token_property_permissions(&self) -> Result<Vec<eth::TokenPropertyPermission>> {136 let perms = <Pallet<T>>::token_property_permission(self.id);137 Ok(perms138 .into_iter()139 .map(eth::TokenPropertyPermission::from)140 .collect())141 }142143 /// @notice Set token property value.144 /// @dev Throws error if `msg.sender` has no permission to edit the property.145 /// @param tokenId ID of the token.146 /// @param key Property key.147 /// @param value Property value.148 #[solidity(hide)]149 #[weight(<SelfWeightOf<T>>::set_token_properties(1))]150 fn set_property(151 &mut self,152 caller: Caller,153 token_id: U256,154 key: String,155 value: Bytes,156 ) -> Result<()> {157 let caller = T::CrossAccountId::from_eth(caller);158 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;159 let key = <Vec<u8>>::from(key)160 .try_into()161 .map_err(|_| "key too long")?;162 let value = value.0.try_into().map_err(|_| "value too long")?;163164 let nesting_budget = self165 .recorder166 .weight_calls_budget(<StructureWeight<T>>::find_parent());167168 <Pallet<T>>::set_token_property(169 self,170 &caller,171 TokenId(token_id),172 Property { key, value },173 &nesting_budget,174 )175 .map_err(dispatch_to_evm::<T>)176 }177178 /// @notice Set token properties value.179 /// @dev Throws error if `msg.sender` has no permission to edit the property.180 /// @param tokenId ID of the token.181 /// @param properties settable properties182 #[weight(<SelfWeightOf<T>>::set_token_properties(properties.len() as u32))]183 fn set_properties(184 &mut self,185 caller: Caller,186 token_id: U256,187 properties: Vec<eth::Property>,188 ) -> Result<()> {189 let caller = T::CrossAccountId::from_eth(caller);190 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;191192 let nesting_budget = self193 .recorder194 .weight_calls_budget(<StructureWeight<T>>::find_parent());195196 let properties = properties197 .into_iter()198 .map(eth::Property::try_into)199 .collect::<Result<Vec<_>>>()?;200201 <Pallet<T>>::set_token_properties(202 self,203 &caller,204 TokenId(token_id),205 properties.into_iter(),206 &nesting_budget,207 )208 .map_err(dispatch_to_evm::<T>)209 }210211 /// @notice Delete token property value.212 /// @dev Throws error if `msg.sender` has no permission to edit the property.213 /// @param tokenId ID of the token.214 /// @param key Property key.215 #[solidity(hide)]216 #[weight(<SelfWeightOf<T>>::delete_token_properties(1))]217 fn delete_property(&mut self, token_id: U256, caller: Caller, key: String) -> Result<()> {218 let caller = T::CrossAccountId::from_eth(caller);219 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;220 let key = <Vec<u8>>::from(key)221 .try_into()222 .map_err(|_| "key too long")?;223224 let nesting_budget = self225 .recorder226 .weight_calls_budget(<StructureWeight<T>>::find_parent());227228 <Pallet<T>>::delete_token_property(self, &caller, TokenId(token_id), key, &nesting_budget)229 .map_err(dispatch_to_evm::<T>)230 }231232 /// @notice Delete token properties value.233 /// @dev Throws error if `msg.sender` has no permission to edit the property.234 /// @param tokenId ID of the token.235 /// @param keys Properties key.236 #[weight(<SelfWeightOf<T>>::delete_token_properties(keys.len() as u32))]237 fn delete_properties(238 &mut self,239 token_id: U256,240 caller: Caller,241 keys: Vec<String>,242 ) -> Result<()> {243 let caller = T::CrossAccountId::from_eth(caller);244 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;245 let keys = keys246 .into_iter()247 .map(|k| Ok(<Vec<u8>>::from(k).try_into().map_err(|_| "key too long")?))248 .collect::<Result<Vec<_>>>()?;249250 let nesting_budget = self251 .recorder252 .weight_calls_budget(<StructureWeight<T>>::find_parent());253254 <Pallet<T>>::delete_token_properties(255 self,256 &caller,257 TokenId(token_id),258 keys.into_iter(),259 &nesting_budget,260 )261 .map_err(dispatch_to_evm::<T>)262 }263264 /// @notice Get token property value.265 /// @dev Throws error if key not found266 /// @param tokenId ID of the token.267 /// @param key Property key.268 /// @return Property value bytes269 fn property(&self, token_id: U256, key: String) -> Result<Bytes> {270 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;271 let key = <Vec<u8>>::from(key)272 .try_into()273 .map_err(|_| "key too long")?;274275 let props = <TokenProperties<T>>::get((self.id, token_id));276 let prop = props.get(&key).ok_or("key not found")?;277278 Ok(prop.to_vec().into())279 }280}281282#[derive(ToLog)]283pub enum ERC721Events {284 /// @dev This emits when ownership of any NFT changes by any mechanism.285 /// This event emits when NFTs are created (`from` == 0) and destroyed286 /// (`to` == 0). Exception: during contract creation, any number of NFTs287 /// may be created and assigned without emitting Transfer. At the time of288 /// any transfer, the approved address for that NFT (if any) is reset to none.289 Transfer {290 #[indexed]291 from: Address,292 #[indexed]293 to: Address,294 #[indexed]295 token_id: U256,296 },297 /// @dev This emits when the approved address for an NFT is changed or298 /// reaffirmed. The zero address indicates there is no approved address.299 /// When a Transfer event emits, this also indicates that the approved300 /// address for that NFT (if any) is reset to none.301 Approval {302 #[indexed]303 owner: Address,304 #[indexed]305 approved: Address,306 #[indexed]307 token_id: U256,308 },309 /// @dev This emits when an operator is enabled or disabled for an owner.310 /// The operator can manage all NFTs of the owner.311 #[allow(dead_code)]312 ApprovalForAll {313 #[indexed]314 owner: Address,315 #[indexed]316 operator: Address,317 approved: bool,318 },319}320321/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension322/// @dev See https://eips.ethereum.org/EIPS/eip-721323#[solidity_interface(name = ERC721Metadata, expect_selector = 0x5b5e139f, enum(derive(PreDispatch)), enum_attr(weight))]324impl<T: Config> NonfungibleHandle<T>325where326 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,327{328 /// @notice A descriptive name for a collection of NFTs in this contract329 /// @dev real implementation of this function lies in `ERC721UniqueExtensions`330 #[solidity(hide, rename_selector = "name")]331 fn name_proxy(&self) -> String {332 self.name()333 }334335 /// @notice An abbreviated name for NFTs in this contract336 /// @dev real implementation of this function lies in `ERC721UniqueExtensions`337 #[solidity(hide, rename_selector = "symbol")]338 fn symbol_proxy(&self) -> String {339 self.symbol()340 }341342 /// @notice A distinct Uniform Resource Identifier (URI) for a given asset.343 ///344 /// @dev If the token has a `url` property and it is not empty, it is returned.345 /// Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.346 /// If the collection property `baseURI` is empty or absent, return "" (empty string)347 /// otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix348 /// otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).349 ///350 /// @return token's const_metadata351 #[solidity(rename_selector = "tokenURI")]352 fn token_uri(&self, token_id: U256) -> Result<String> {353 let token_id_u32: u32 = token_id.try_into().map_err(|_| "token id overflow")?;354355 match get_token_property(self, token_id_u32, &key::url()).as_deref() {356 Err(_) | Ok("") => (),357 Ok(url) => {358 return Ok(url.into());359 }360 };361362 let base_uri =363 pallet_common::Pallet::<T>::get_collection_property(self.id, &key::base_uri())364 .map(BoundedVec::into_inner)365 .map(String::from_utf8)366 .transpose()367 .map_err(|e| {368 Error::Revert(alloc::format!(369 "Can not convert value \"baseURI\" to string with error \"{e}\""370 ))371 })?;372373 let base_uri = match base_uri.as_deref() {374 None | Some("") => {375 return Ok("".into());376 }377 Some(base_uri) => base_uri.into(),378 };379380 Ok(381 match get_token_property(self, token_id_u32, &key::suffix()).as_deref() {382 Err(_) | Ok("") => base_uri,383 Ok(suffix) => base_uri + suffix,384 },385 )386 }387}388389/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension390/// @dev See https://eips.ethereum.org/EIPS/eip-721391#[solidity_interface(name = ERC721Enumerable, expect_selector = 0x780e9d63, enum(derive(PreDispatch)), enum_attr(weight))]392impl<T: Config> NonfungibleHandle<T> {393 /// @notice Enumerate valid NFTs394 /// @param index A counter less than `totalSupply()`395 /// @return The token identifier for the `index`th NFT,396 /// (sort order not specified)397 fn token_by_index(&self, index: U256) -> U256 {398 index399 }400401 /// @dev Not implemented402 fn token_of_owner_by_index(&self, _owner: Address, _index: U256) -> Result<U256> {403 // TODO: Not implemetable404 Err("not implemented".into())405 }406407 /// @notice Count NFTs tracked by this contract408 /// @return A count of valid NFTs tracked by this contract, where each one of409 /// them has an assigned and queryable owner not equal to the zero address410 fn total_supply(&self) -> Result<U256> {411 self.consume_store_reads(1)?;412 Ok(<Pallet<T>>::total_supply(self).into())413 }414}415416/// @title ERC-721 Non-Fungible Token Standard417/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md418#[solidity_interface(name = ERC721, events(ERC721Events), enum(derive(PreDispatch)), enum_attr(weight), expect_selector = 0x80ac58cd)]419impl<T: Config> NonfungibleHandle<T> {420 /// @notice Count all NFTs assigned to an owner421 /// @dev NFTs assigned to the zero address are considered invalid, and this422 /// function throws for queries about the zero address.423 /// @param owner An address for whom to query the balance424 /// @return The number of NFTs owned by `owner`, possibly zero425 fn balance_of(&self, owner: Address) -> Result<U256> {426 self.consume_store_reads(1)?;427 let owner = T::CrossAccountId::from_eth(owner);428 let balance = <AccountBalance<T>>::get((self.id, owner));429 Ok(balance.into())430 }431 /// @notice Find the owner of an NFT432 /// @dev NFTs assigned to zero address are considered invalid, and queries433 /// about them do throw.434 /// @param tokenId The identifier for an NFT435 /// @return The address of the owner of the NFT436 fn owner_of(&self, token_id: U256) -> Result<Address> {437 self.consume_store_reads(1)?;438 let token: TokenId = token_id.try_into()?;439 Ok(*<TokenData<T>>::get((self.id, token))440 .ok_or("token not found")?441 .owner442 .as_eth())443 }444 /// @dev Not implemented445 #[solidity(rename_selector = "safeTransferFrom")]446 fn safe_transfer_from_with_data(447 &mut self,448 _from: Address,449 _to: Address,450 _token_id: U256,451 _data: Bytes,452 ) -> Result<()> {453 // TODO: Not implemetable454 Err("not implemented".into())455 }456 /// @dev Not implemented457 fn safe_transfer_from(&mut self, _from: Address, _to: Address, _token_id: U256) -> Result<()> {458 // TODO: Not implemetable459 Err("not implemented".into())460 }461462 /// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE463 /// TO CONFIRM THAT `to` IS CAPABLE OF RECEIVING NFTS OR ELSE464 /// THEY MAY BE PERMANENTLY LOST465 /// @dev Throws unless `msg.sender` is the current owner or an authorized466 /// operator for this NFT. Throws if `from` is not the current owner. Throws467 /// if `to` is the zero address. Throws if `tokenId` is not a valid NFT.468 /// @param from The current owner of the NFT469 /// @param to The new owner470 /// @param tokenId The NFT to transfer471 #[weight(<CommonWeights<T>>::transfer_from())]472 fn transfer_from(473 &mut self,474 caller: Caller,475 from: Address,476 to: Address,477 token_id: U256,478 ) -> Result<()> {479 let caller = T::CrossAccountId::from_eth(caller);480 let from = T::CrossAccountId::from_eth(from);481 let to = T::CrossAccountId::from_eth(to);482 let token = token_id.try_into()?;483 let budget = self484 .recorder485 .weight_calls_budget(<StructureWeight<T>>::find_parent());486487 <Pallet<T>>::transfer_from(self, &caller, &from, &to, token, &budget)488 .map_err(|e| dispatch_to_evm::<T>(e.error))?;489 Ok(())490 }491492 /// @notice Set or reaffirm the approved address for an NFT493 /// @dev The zero address indicates there is no approved address.494 /// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized495 /// operator of the current owner.496 /// @param approved The new approved NFT controller497 /// @param tokenId The NFT to approve498 #[weight(<SelfWeightOf<T>>::approve())]499 fn approve(&mut self, caller: Caller, approved: Address, token_id: U256) -> Result<()> {500 let caller = T::CrossAccountId::from_eth(caller);501 let approved = T::CrossAccountId::from_eth(approved);502 let token = token_id.try_into()?;503504 <Pallet<T>>::set_allowance(self, &caller, token, Some(&approved))505 .map_err(dispatch_to_evm::<T>)?;506 Ok(())507 }508509 /// @notice Sets or unsets the approval of a given operator.510 /// The `operator` is allowed to transfer all tokens of the `caller` on their behalf.511 /// @param operator Operator512 /// @param approved Should operator status be granted or revoked?513 #[weight(<SelfWeightOf<T>>::set_allowance_for_all())]514 fn set_approval_for_all(515 &mut self,516 caller: Caller,517 operator: Address,518 approved: bool,519 ) -> Result<()> {520 let caller = T::CrossAccountId::from_eth(caller);521 let operator = T::CrossAccountId::from_eth(operator);522523 <Pallet<T>>::set_allowance_for_all(self, &caller, &operator, approved)524 .map_err(dispatch_to_evm::<T>)?;525 Ok(())526 }527528 /// @notice Get the approved address for a single NFT529 /// @dev Throws if `tokenId` is not a valid NFT530 /// @param tokenId The NFT to find the approved address for531 /// @return The approved address for this NFT, or the zero address if there is none532 fn get_approved(&self, token_id: U256) -> Result<Address> {533 let token_id = token_id.try_into()?;534 let operator = <Pallet<T>>::get_allowance(self, token_id).map_err(dispatch_to_evm::<T>)?;535 Ok(if let Some(operator) = operator {536 *operator.as_eth()537 } else {538 Address::zero()539 })540 }541542 /// @notice Tells whether the given `owner` approves the `operator`.543 #[weight(<SelfWeightOf<T>>::allowance_for_all())]544 fn is_approved_for_all(&self, owner: Address, operator: Address) -> Result<bool> {545 let owner = T::CrossAccountId::from_eth(owner);546 let operator = T::CrossAccountId::from_eth(operator);547548 Ok(<Pallet<T>>::allowance_for_all(self, &owner, &operator))549 }550}551552/// @title ERC721 Token that can be irreversibly burned (destroyed).553#[solidity_interface(name = ERC721Burnable, enum(derive(PreDispatch)), enum_attr(weight))]554impl<T: Config> NonfungibleHandle<T> {555 /// @notice Burns a specific ERC721 token.556 /// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized557 /// operator of the current owner.558 /// @param tokenId The NFT to approve559 #[weight(<SelfWeightOf<T>>::burn_item())]560 fn burn(&mut self, caller: Caller, token_id: U256) -> Result<()> {561 let caller = T::CrossAccountId::from_eth(caller);562 let token = token_id.try_into()?;563564 <Pallet<T>>::burn(self, &caller, token).map_err(dispatch_to_evm::<T>)?;565 Ok(())566 }567}568569/// @title ERC721 minting logic.570#[solidity_interface(name = ERC721UniqueMintable, enum(derive(PreDispatch)), enum_attr(weight))]571impl<T: Config> NonfungibleHandle<T> {572 /// @notice Function to mint a token.573 /// @param to The new owner574 /// @return uint256 The id of the newly minted token575 #[weight(<SelfWeightOf<T>>::create_item())]576 fn mint(&mut self, caller: Caller, to: Address) -> Result<U256> {577 let token_id: U256 = <TokensMinted<T>>::get(self.id)578 .checked_add(1)579 .ok_or("item id overflow")?580 .into();581 self.mint_check_id(caller, to, token_id)?;582 Ok(token_id)583 }584585 /// @notice Function to mint a token.586 /// @dev `tokenId` should be obtained with `nextTokenId` method,587 /// unlike standard, you can't specify it manually588 /// @param to The new owner589 /// @param tokenId ID of the minted NFT590 #[solidity(hide, rename_selector = "mint")]591 #[weight(<SelfWeightOf<T>>::create_item())]592 fn mint_check_id(&mut self, caller: Caller, to: Address, token_id: U256) -> Result<bool> {593 let caller = T::CrossAccountId::from_eth(caller);594 let to = T::CrossAccountId::from_eth(to);595 let token_id: u32 = token_id.try_into()?;596 let budget = self597 .recorder598 .weight_calls_budget(<StructureWeight<T>>::find_parent());599600 if <TokensMinted<T>>::get(self.id)601 .checked_add(1)602 .ok_or("item id overflow")?603 != token_id604 {605 return Err("item id should be next".into());606 }607608 <Pallet<T>>::create_item(609 self,610 &caller,611 CreateItemData::<T> {612 properties: BoundedVec::default(),613 owner: to,614 },615 &budget,616 )617 .map_err(dispatch_to_evm::<T>)?;618619 Ok(true)620 }621622 /// @notice Function to mint token with the given tokenUri.623 /// @param to The new owner624 /// @param tokenUri Token URI that would be stored in the NFT properties625 /// @return uint256 The id of the newly minted token626 #[solidity(rename_selector = "mintWithTokenURI")]627 #[weight(<SelfWeightOf<T>>::create_item() + <SelfWeightOf<T>>::set_token_properties(1))]628 fn mint_with_token_uri(629 &mut self,630 caller: Caller,631 to: Address,632 token_uri: String,633 ) -> Result<U256> {634 let token_id: U256 = <TokensMinted<T>>::get(self.id)635 .checked_add(1)636 .ok_or("item id overflow")?637 .into();638 self.mint_with_token_uri_check_id(caller, to, token_id, token_uri)?;639 Ok(token_id)640 }641642 /// @notice Function to mint token with the given tokenUri.643 /// @dev `tokenId` should be obtained with `nextTokenId` method,644 /// unlike standard, you can't specify it manually645 /// @param to The new owner646 /// @param tokenId ID of the minted NFT647 /// @param tokenUri Token URI that would be stored in the NFT properties648 #[solidity(hide, rename_selector = "mintWithTokenURI")]649 #[weight(<SelfWeightOf<T>>::create_item() + <SelfWeightOf<T>>::set_token_properties(1))]650 fn mint_with_token_uri_check_id(651 &mut self,652 caller: Caller,653 to: Address,654 token_id: U256,655 token_uri: String,656 ) -> Result<bool> {657 let key = key::url();658 let permission = get_token_permission::<T>(self.id, &key)?;659 if !permission.collection_admin {660 return Err("Operation is not allowed".into());661 }662663 let caller = T::CrossAccountId::from_eth(caller);664 let to = T::CrossAccountId::from_eth(to);665 let token_id: u32 = token_id.try_into().map_err(|_| "amount overflow")?;666 let budget = self667 .recorder668 .weight_calls_budget(<StructureWeight<T>>::find_parent());669670 if <TokensMinted<T>>::get(self.id)671 .checked_add(1)672 .ok_or("item id overflow")?673 != token_id674 {675 return Err("item id should be next".into());676 }677678 let mut properties = CollectionPropertiesVec::default();679 properties680 .try_push(Property {681 key,682 value: token_uri683 .into_bytes()684 .try_into()685 .map_err(|_| "token uri is too long")?,686 })687 .map_err(|e| Error::Revert(alloc::format!("Can't add property: {e:?}")))?;688689 <Pallet<T>>::create_item(690 self,691 &caller,692 CreateItemData::<T> {693 properties,694 owner: to,695 },696 &budget,697 )698 .map_err(dispatch_to_evm::<T>)?;699 Ok(true)700 }701}702703fn get_token_property<T: Config>(704 collection: &CollectionHandle<T>,705 token_id: u32,706 key: &up_data_structs::PropertyKey,707) -> Result<String> {708 collection.consume_store_reads(1)?;709 let properties = <TokenProperties<T>>::try_get((collection.id, token_id))710 .map_err(|_| Error::Revert("Token properties not found".into()))?;711 if let Some(property) = properties.get(key) {712 return Ok(String::from_utf8_lossy(property).into());713 }714715 Err("Property tokenURI not found".into())716}717718fn get_token_permission<T: Config>(719 collection_id: CollectionId,720 key: &PropertyKey,721) -> Result<PropertyPermission> {722 let token_property_permissions = CollectionPropertyPermissions::<T>::try_get(collection_id)723 .map_err(|_| Error::Revert("No permissions for collection".into()))?;724 let a = token_property_permissions725 .get(key)726 .map(Clone::clone)727 .ok_or_else(|| {728 let key = String::from_utf8(key.clone().into_inner()).unwrap_or_default();729 Error::Revert(alloc::format!("No permission for key {key}"))730 })?;731 Ok(a)732}733734/// @title Unique extensions for ERC721.735#[solidity_interface(name = ERC721UniqueExtensions, enum(derive(PreDispatch)), enum_attr(weight))]736impl<T: Config> NonfungibleHandle<T>737where738 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,739{740 /// @notice A descriptive name for a collection of NFTs in this contract741 fn name(&self) -> String {742 decode_utf16(self.name.iter().copied())743 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))744 .collect::<String>()745 }746747 /// @notice An abbreviated name for NFTs in this contract748 fn symbol(&self) -> String {749 String::from_utf8_lossy(&self.token_prefix).into()750 }751752 /// @notice A description for the collection.753 fn description(&self) -> String {754 decode_utf16(self.description.iter().copied())755 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))756 .collect::<String>()757 }758759 /// Returns the owner (in cross format) of the token.760 ///761 /// @param tokenId Id for the token.762 #[solidity(hide)]763 fn cross_owner_of(&self, token_id: U256) -> Result<eth::CrossAddress> {764 Self::owner_of_cross(self, token_id)765 }766767 /// Returns the owner (in cross format) of the token.768 ///769 /// @param tokenId Id for the token.770 fn owner_of_cross(&self, token_id: U256) -> Result<eth::CrossAddress> {771 Self::token_owner(self, token_id.try_into()?)772 .map(|o| eth::CrossAddress::from_sub_cross_account::<T>(&o))773 .map_err(|_| Error::Revert("token not found".into()))774 }775776 /// @notice Count all NFTs assigned to an owner777 /// @param owner An cross address for whom to query the balance778 /// @return The number of NFTs owned by `owner`, possibly zero779 fn balance_of_cross(&self, owner: eth::CrossAddress) -> Result<U256> {780 self.consume_store_reads(1)?;781 let balance = <AccountBalance<T>>::get((self.id, owner.into_sub_cross_account::<T>()?));782 Ok(balance.into())783 }784785 /// Returns the token properties.786 ///787 /// @param tokenId Id for the token.788 /// @param keys Properties keys. Empty keys for all propertyes.789 /// @return Vector of properties key/value pairs.790 fn properties(&self, token_id: U256, keys: Vec<String>) -> Result<Vec<eth::Property>> {791 let keys = keys792 .into_iter()793 .map(|key| {794 <Vec<u8>>::from(key)795 .try_into()796 .map_err(|_| Error::Revert("key too large".into()))797 })798 .collect::<Result<Vec<_>>>()?;799800 <Self as CommonCollectionOperations<T>>::token_properties(801 self,802 token_id.try_into()?,803 if keys.is_empty() { None } else { Some(keys) },804 )805 .into_iter()806 .map(eth::Property::try_from)807 .collect::<Result<Vec<_>>>()808 }809810 /// @notice Set or reaffirm the approved address for an NFT811 /// @dev The zero address indicates there is no approved address.812 /// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized813 /// operator of the current owner.814 /// @param approved The new substrate address approved NFT controller815 /// @param tokenId The NFT to approve816 #[weight(<SelfWeightOf<T>>::approve())]817 fn approve_cross(818 &mut self,819 caller: Caller,820 approved: eth::CrossAddress,821 token_id: U256,822 ) -> Result<()> {823 let caller = T::CrossAccountId::from_eth(caller);824 let approved = approved.into_sub_cross_account::<T>()?;825 let token = token_id.try_into()?;826827 <Pallet<T>>::set_allowance(self, &caller, token, Some(&approved))828 .map_err(dispatch_to_evm::<T>)?;829 Ok(())830 }831832 /// @notice Transfer ownership of an NFT833 /// @dev Throws unless `msg.sender` is the current owner. Throws if `to`834 /// is the zero address. Throws if `tokenId` is not a valid NFT.835 /// @param to The new owner836 /// @param tokenId The NFT to transfer837 #[weight(<CommonWeights<T>>::transfer())]838 fn transfer(&mut self, caller: Caller, to: Address, token_id: U256) -> Result<()> {839 let caller = T::CrossAccountId::from_eth(caller);840 let to = T::CrossAccountId::from_eth(to);841 let token = token_id.try_into()?;842 let budget = self843 .recorder844 .weight_calls_budget(<StructureWeight<T>>::find_parent());845846 <Pallet<T>>::transfer(self, &caller, &to, token, &budget)847 .map_err(|e| dispatch_to_evm::<T>(e.error))?;848 Ok(())849 }850851 /// @notice Transfer ownership of an NFT852 /// @dev Throws unless `msg.sender` is the current owner. Throws if `to`853 /// is the zero address. Throws if `tokenId` is not a valid NFT.854 /// @param to The new owner855 /// @param tokenId The NFT to transfer856 #[weight(<CommonWeights<T>>::transfer())]857 fn transfer_cross(858 &mut self,859 caller: Caller,860 to: eth::CrossAddress,861 token_id: U256,862 ) -> Result<()> {863 let caller = T::CrossAccountId::from_eth(caller);864 let to = to.into_sub_cross_account::<T>()?;865 let token = token_id.try_into()?;866 let budget = self867 .recorder868 .weight_calls_budget(<StructureWeight<T>>::find_parent());869870 <Pallet<T>>::transfer(self, &caller, &to, token, &budget)871 .map_err(|e| dispatch_to_evm::<T>(e.error))?;872 Ok(())873 }874875 /// @notice Transfer ownership of an NFT from cross account address to cross account address876 /// @dev Throws unless `msg.sender` is the current owner. Throws if `to`877 /// is the zero address. Throws if `tokenId` is not a valid NFT.878 /// @param from Cross acccount address of current owner879 /// @param to Cross acccount address of new owner880 /// @param tokenId The NFT to transfer881 #[weight(<CommonWeights<T>>::transfer_from())]882 fn transfer_from_cross(883 &mut self,884 caller: Caller,885 from: eth::CrossAddress,886 to: eth::CrossAddress,887 token_id: U256,888 ) -> Result<()> {889 let caller = T::CrossAccountId::from_eth(caller);890 let from = from.into_sub_cross_account::<T>()?;891 let to = to.into_sub_cross_account::<T>()?;892 let token_id = token_id.try_into()?;893 let budget = self894 .recorder895 .weight_calls_budget(<StructureWeight<T>>::find_parent());896 Pallet::<T>::transfer_from(self, &caller, &from, &to, token_id, &budget)897 .map_err(|e| dispatch_to_evm::<T>(e.error))?;898 Ok(())899 }900901 /// @notice Burns a specific ERC721 token.902 /// @dev Throws unless `msg.sender` is the current owner or an authorized903 /// operator for this NFT. Throws if `from` is not the current owner. Throws904 /// if `to` is the zero address. Throws if `tokenId` is not a valid NFT.905 /// @param from The current owner of the NFT906 /// @param tokenId The NFT to transfer907 #[solidity(hide)]908 #[weight(<SelfWeightOf<T>>::burn_from())]909 fn burn_from(&mut self, caller: Caller, from: Address, token_id: U256) -> Result<()> {910 let caller = T::CrossAccountId::from_eth(caller);911 let from = T::CrossAccountId::from_eth(from);912 let token = token_id.try_into()?;913 let budget = self914 .recorder915 .weight_calls_budget(<StructureWeight<T>>::find_parent());916917 <Pallet<T>>::burn_from(self, &caller, &from, token, &budget)918 .map_err(dispatch_to_evm::<T>)?;919 Ok(())920 }921922 /// @notice Burns a specific ERC721 token.923 /// @dev Throws unless `msg.sender` is the current owner or an authorized924 /// operator for this NFT. Throws if `from` is not the current owner. Throws925 /// if `to` is the zero address. Throws if `tokenId` is not a valid NFT.926 /// @param from The current owner of the NFT927 /// @param tokenId The NFT to transfer928 #[weight(<SelfWeightOf<T>>::burn_from())]929 fn burn_from_cross(930 &mut self,931 caller: Caller,932 from: eth::CrossAddress,933 token_id: U256,934 ) -> Result<()> {935 let caller = T::CrossAccountId::from_eth(caller);936 let from = from.into_sub_cross_account::<T>()?;937 let token = token_id.try_into()?;938 let budget = self939 .recorder940 .weight_calls_budget(<StructureWeight<T>>::find_parent());941942 <Pallet<T>>::burn_from(self, &caller, &from, token, &budget)943 .map_err(dispatch_to_evm::<T>)?;944 Ok(())945 }946947 /// @notice Returns next free NFT ID.948 fn next_token_id(&self) -> Result<U256> {949 self.consume_store_reads(1)?;950 Ok(<Pallet<T>>::next_token_id(self)951 .map_err(dispatch_to_evm::<T>)?952 .into())953 }954955 /// @notice Function to mint multiple tokens.956 /// @dev `tokenIds` should be an array of consecutive numbers and first number957 /// should be obtained with `nextTokenId` method958 /// @param to The new owner959 /// @param tokenIds IDs of the minted NFTs960 #[solidity(hide)]961 #[weight(<SelfWeightOf<T>>::create_multiple_items(token_ids.len() as u32))]962 fn mint_bulk(&mut self, caller: Caller, to: Address, token_ids: Vec<U256>) -> Result<bool> {963 let caller = T::CrossAccountId::from_eth(caller);964 let to = T::CrossAccountId::from_eth(to);965 let mut expected_index = <TokensMinted<T>>::get(self.id)966 .checked_add(1)967 .ok_or("item id overflow")?;968 let budget = self969 .recorder970 .weight_calls_budget(<StructureWeight<T>>::find_parent());971972 let total_tokens = token_ids.len();973 for id in token_ids.into_iter() {974 let id: u32 = id.try_into().map_err(|_| "token id overflow")?;975 if id != expected_index {976 return Err("item id should be next".into());977 }978 expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;979 }980 let data = (0..total_tokens)981 .map(|_| CreateItemData::<T> {982 properties: BoundedVec::default(),983 owner: to.clone(),984 })985 .collect();986987 <Pallet<T>>::create_multiple_items(self, &caller, data, &budget)988 .map_err(dispatch_to_evm::<T>)?;989 Ok(true)990 }991992 /// @notice Function to mint a token.993 /// @param data Array of pairs of token owner and token's properties for minted token994 #[weight(<SelfWeightOf<T>>::create_multiple_items(data.len() as u32) + <SelfWeightOf<T>>::set_token_properties(data.len() as u32))]995 fn mint_bulk_cross(&mut self, caller: Caller, data: Vec<MintTokenData>) -> Result<bool> {996 let caller = T::CrossAccountId::from_eth(caller);997 let budget = self998 .recorder999 .weight_calls_budget(<StructureWeight<T>>::find_parent());10001001 let mut create_nft_data = Vec::with_capacity(data.len());1002 for MintTokenData { owner, properties } in data {1003 let owner = owner.into_sub_cross_account::<T>()?;1004 create_nft_data.push(CreateItemData::<T> {1005 properties: properties1006 .into_iter()1007 .map(|property| property.try_into())1008 .collect::<Result<Vec<_>>>()?1009 .try_into()1010 .map_err(|_| "too many properties")?,1011 owner,1012 });1013 }10141015 <Pallet<T>>::create_multiple_items(self, &caller, create_nft_data, &budget)1016 .map_err(dispatch_to_evm::<T>)?;1017 Ok(true)1018 }10191020 /// @notice Function to mint multiple tokens with the given tokenUris.1021 /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive1022 /// numbers and first number should be obtained with `nextTokenId` method1023 /// @param to The new owner1024 /// @param tokens array of pairs of token ID and token URI for minted tokens1025 #[solidity(hide, rename_selector = "mintBulkWithTokenURI")]1026 #[weight(<SelfWeightOf<T>>::create_multiple_items(tokens.len() as u32) + <SelfWeightOf<T>>::set_token_properties(tokens.len() as u32))]1027 fn mint_bulk_with_token_uri(1028 &mut self,1029 caller: Caller,1030 to: Address,1031 tokens: Vec<TokenUri>,1032 ) -> Result<bool> {1033 let key = key::url();1034 let caller = T::CrossAccountId::from_eth(caller);1035 let to = T::CrossAccountId::from_eth(to);1036 let mut expected_index = <TokensMinted<T>>::get(self.id)1037 .checked_add(1)1038 .ok_or("item id overflow")?;1039 let budget = self1040 .recorder1041 .weight_calls_budget(<StructureWeight<T>>::find_parent());10421043 let mut data = Vec::with_capacity(tokens.len());1044 for TokenUri { id, uri } in tokens {1045 let id: u32 = id.try_into().map_err(|_| "token id overflow")?;1046 if id != expected_index {1047 return Err("item id should be next".into());1048 }1049 expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;10501051 let mut properties = CollectionPropertiesVec::default();1052 properties1053 .try_push(Property {1054 key: key.clone(),1055 value: uri1056 .into_bytes()1057 .try_into()1058 .map_err(|_| "token uri is too long")?,1059 })1060 .map_err(|e| Error::Revert(alloc::format!("Can't add property: {e:?}")))?;10611062 data.push(CreateItemData::<T> {1063 properties,1064 owner: to.clone(),1065 });1066 }10671068 <Pallet<T>>::create_multiple_items(self, &caller, data, &budget)1069 .map_err(dispatch_to_evm::<T>)?;1070 Ok(true)1071 }10721073 /// @notice Function to mint a token.1074 /// @param to The new owner crossAccountId1075 /// @param properties Properties of minted token1076 /// @return uint256 The id of the newly minted token1077 #[weight(<SelfWeightOf<T>>::create_item() + <SelfWeightOf<T>>::set_token_properties(properties.len() as u32))]1078 fn mint_cross(1079 &mut self,1080 caller: Caller,1081 to: eth::CrossAddress,1082 properties: Vec<eth::Property>,1083 ) -> Result<U256> {1084 let token_id = <TokensMinted<T>>::get(self.id)1085 .checked_add(1)1086 .ok_or("item id overflow")?;10871088 let to = to.into_sub_cross_account::<T>()?;10891090 let properties = properties1091 .into_iter()1092 .map(eth::Property::try_into)1093 .collect::<Result<Vec<_>>>()?1094 .try_into()1095 .map_err(|_| Error::Revert("too many properties".to_string()))?;10961097 let caller = T::CrossAccountId::from_eth(caller);10981099 let budget = self1100 .recorder1101 .weight_calls_budget(<StructureWeight<T>>::find_parent());11021103 <Pallet<T>>::create_item(1104 self,1105 &caller,1106 CreateItemData::<T> {1107 properties,1108 owner: to,1109 },1110 &budget,1111 )1112 .map_err(dispatch_to_evm::<T>)?;11131114 Ok(token_id.into())1115 }11161117 /// @notice Returns collection helper contract address1118 fn collection_helper_address(&self) -> Address {1119 T::ContractAddress::get()1120 }1121}11221123#[solidity_interface(1124 name = UniqueNFT,1125 is(1126 ERC721,1127 ERC721Enumerable,1128 ERC721UniqueExtensions,1129 ERC721UniqueMintable,1130 ERC721Burnable,1131 ERC721Metadata(if(this.flags.erc721metadata)),1132 Collection(via(common_mut returns CollectionHandle<T>)),1133 TokenProperties,1134 ),1135 enum(derive(PreDispatch)),1136)]1137impl<T: Config> NonfungibleHandle<T> where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]> {}11381139// Not a tests, but code generators1140generate_stubgen!(gen_impl, UniqueNFTCall<()>, true);1141generate_stubgen!(gen_iface, UniqueNFTCall<()>, false);11421143impl<T: Config> CommonEvmHandler for NonfungibleHandle<T>1144where1145 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,1146{1147 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueNFT.raw");11481149 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {1150 call::<T, UniqueNFTCall<T>, _, _>(handle, self)1151 }1152}pallets/nonfungible/src/lib.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -102,8 +102,8 @@
use up_data_structs::{
AccessMode, CollectionId, CustomDataLimit, TokenId, CreateCollectionData, CreateNftExData,
mapping::TokenAddressMapping, budget::Budget, Property, PropertyKey, PropertyValue,
- PropertyKeyPermission, PropertyScope, TrySetProperty, TokenChild, AuxPropertyValue,
- PropertiesPermissionMap, TokenProperties as TokenPropertiesT,
+ PropertyKeyPermission, PropertyScope, TokenChild, AuxPropertyValue, PropertiesPermissionMap,
+ TokenProperties as TokenPropertiesT,
};
use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};
use pallet_common::{
@@ -201,7 +201,7 @@
pub type TokenProperties<T: Config> = StorageNMap<
Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),
Value = TokenPropertiesT,
- QueryKind = ValueQuery,
+ QueryKind = OptionQuery,
>;
/// Custom data of a token that is serialized to bytes,
@@ -340,40 +340,8 @@
/// - `token`: Token ID.
pub fn token_exists(collection: &NonfungibleHandle<T>, token: TokenId) -> bool {
<TokenData<T>>::contains_key((collection.id, token))
- }
-
- /// Set the token property with the scope.
- ///
- /// - `property`: Contains key-value pair.
- pub fn set_scoped_token_property(
- collection_id: CollectionId,
- token_id: TokenId,
- scope: PropertyScope,
- property: Property,
- ) -> DispatchResult {
- TokenProperties::<T>::try_mutate((collection_id, token_id), |properties| {
- properties.try_scoped_set(scope, property.key, property.value)
- })
- .map_err(<CommonError<T>>::from)?;
-
- Ok(())
}
- /// Batch operation to set multiple properties with the same scope.
- pub fn set_scoped_token_properties(
- collection_id: CollectionId,
- token_id: TokenId,
- scope: PropertyScope,
- properties: impl Iterator<Item = Property>,
- ) -> DispatchResult {
- TokenProperties::<T>::try_mutate((collection_id, token_id), |stored_properties| {
- stored_properties.try_scoped_set_from_iter(scope, properties)
- })
- .map_err(<CommonError<T>>::from)?;
-
- Ok(())
- }
-
/// Add or edit auxiliary data for the property.
///
/// - `f`: function that adds or edits auxiliary data.
@@ -1394,7 +1362,9 @@
pub fn repair_item(collection: &NonfungibleHandle<T>, token: TokenId) -> DispatchResult {
<TokenProperties<T>>::mutate((collection.id, token), |properties| {
- properties.recompute_consumed_space();
+ if let Some(properties) = properties {
+ properties.recompute_consumed_space();
+ }
});
Ok(())
pallets/refungible/src/common.rsdiffbeforeafterboth--- a/pallets/refungible/src/common.rs
+++ b/pallets/refungible/src/common.rs
@@ -435,16 +435,15 @@
)
}
- fn get_token_properties_map(&self, token_id: TokenId) -> up_data_structs::TokenProperties {
+ fn get_token_properties_raw(
+ &self,
+ token_id: TokenId,
+ ) -> Option<up_data_structs::TokenProperties> {
<TokenProperties<T>>::get((self.id, token_id))
}
- fn set_token_properties_map(&self, token_id: TokenId, map: up_data_structs::TokenProperties) {
- <TokenProperties<T>>::set((self.id, token_id), map)
- }
-
- fn properties_exist(&self, token: TokenId) -> bool {
- <TokenProperties<T>>::contains_key((self.id, token))
+ fn set_token_properties_raw(&self, token_id: TokenId, map: up_data_structs::TokenProperties) {
+ <TokenProperties<T>>::insert((self.id, token_id), map)
}
fn check_nesting(
@@ -514,13 +513,15 @@
}
fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue> {
- <Pallet<T>>::token_properties((self.id, token_id))
+ <Pallet<T>>::token_properties((self.id, token_id))?
.get(key)
.cloned()
}
fn token_properties(&self, token_id: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property> {
- let properties = <Pallet<T>>::token_properties((self.id, token_id));
+ let Some(properties) = <Pallet<T>>::token_properties((self.id, token_id)) else {
+ return vec![];
+ };
keys.map(|keys| {
keys.into_iter()
pallets/refungible/src/erc.rsdiffbeforeafterboth--- a/pallets/refungible/src/erc.rs
+++ b/pallets/refungible/src/erc.rs
@@ -283,7 +283,8 @@
.try_into()
.map_err(|_| "key too long")?;
- let props = <TokenProperties<T>>::get((self.id, token_id));
+ let props =
+ <TokenProperties<T>>::get((self.id, token_id)).ok_or("Token properties not found")?;
let prop = props.get(&key).ok_or("key not found")?;
Ok(prop.to_vec().into())
pallets/refungible/src/lib.rsdiffbeforeafterboth--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -106,8 +106,8 @@
use up_data_structs::{
AccessMode, budget::Budget, CollectionId, CreateCollectionData, mapping::TokenAddressMapping,
MAX_REFUNGIBLE_PIECES, Property, PropertyKey, PropertyKeyPermission, PropertyScope,
- PropertyValue, TokenId, TrySetProperty, PropertiesPermissionMap,
- CreateRefungibleExMultipleOwners, TokenOwnerError, TokenProperties as TokenPropertiesT,
+ PropertyValue, TokenId, PropertiesPermissionMap, CreateRefungibleExMultipleOwners,
+ TokenOwnerError, TokenProperties as TokenPropertiesT,
};
pub use pallet::*;
@@ -175,7 +175,7 @@
pub type TokenProperties<T: Config> = StorageNMap<
Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),
Value = TokenPropertiesT,
- QueryKind = ValueQuery,
+ QueryKind = OptionQuery,
>;
/// Total amount of pieces for token
@@ -292,35 +292,7 @@
/// - `token`: Token ID.
pub fn token_exists(collection: &RefungibleHandle<T>, token: TokenId) -> bool {
<TotalSupply<T>>::contains_key((collection.id, token))
- }
-
- pub fn set_scoped_token_property(
- collection_id: CollectionId,
- token_id: TokenId,
- scope: PropertyScope,
- property: Property,
- ) -> DispatchResult {
- TokenProperties::<T>::try_mutate((collection_id, token_id), |properties| {
- properties.try_scoped_set(scope, property.key, property.value)
- })
- .map_err(<CommonError<T>>::from)?;
-
- Ok(())
}
-
- pub fn set_scoped_token_properties(
- collection_id: CollectionId,
- token_id: TokenId,
- scope: PropertyScope,
- properties: impl Iterator<Item = Property>,
- ) -> DispatchResult {
- TokenProperties::<T>::try_mutate((collection_id, token_id), |stored_properties| {
- stored_properties.try_scoped_set_from_iter(scope, properties)
- })
- .map_err(<CommonError<T>>::from)?;
-
- Ok(())
- }
}
// unchecked calls skips any permission checks
@@ -1426,7 +1398,9 @@
pub fn repair_item(collection: &RefungibleHandle<T>, token: TokenId) -> DispatchResult {
<TokenProperties<T>>::mutate((collection.id, token), |properties| {
- properties.recompute_consumed_space();
+ if let Some(properties) = properties {
+ properties.recompute_consumed_space();
+ }
});
Ok(())