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 =276 <TokenProperties<T>>::get((self.id, token_id)).ok_or("Token properties not found")?;277 let prop = props.get(&key).ok_or("key not found")?;278279 Ok(prop.to_vec().into())280 }281}282283#[derive(ToLog)]284pub enum ERC721Events {285 /// @dev This emits when ownership of any NFT changes by any mechanism.286 /// This event emits when NFTs are created (`from` == 0) and destroyed287 /// (`to` == 0). Exception: during contract creation, any number of NFTs288 /// may be created and assigned without emitting Transfer. At the time of289 /// any transfer, the approved address for that NFT (if any) is reset to none.290 Transfer {291 #[indexed]292 from: Address,293 #[indexed]294 to: Address,295 #[indexed]296 token_id: U256,297 },298 /// @dev This emits when the approved address for an NFT is changed or299 /// reaffirmed. The zero address indicates there is no approved address.300 /// When a Transfer event emits, this also indicates that the approved301 /// address for that NFT (if any) is reset to none.302 Approval {303 #[indexed]304 owner: Address,305 #[indexed]306 approved: Address,307 #[indexed]308 token_id: U256,309 },310 /// @dev This emits when an operator is enabled or disabled for an owner.311 /// The operator can manage all NFTs of the owner.312 #[allow(dead_code)]313 ApprovalForAll {314 #[indexed]315 owner: Address,316 #[indexed]317 operator: Address,318 approved: bool,319 },320}321322/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension323/// @dev See https://eips.ethereum.org/EIPS/eip-721324#[solidity_interface(name = ERC721Metadata, expect_selector = 0x5b5e139f, enum(derive(PreDispatch)), enum_attr(weight))]325impl<T: Config> NonfungibleHandle<T>326where327 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,328{329 /// @notice A descriptive name for a collection of NFTs in this contract330 /// @dev real implementation of this function lies in `ERC721UniqueExtensions`331 #[solidity(hide, rename_selector = "name")]332 fn name_proxy(&self) -> String {333 self.name()334 }335336 /// @notice An abbreviated name for NFTs in this contract337 /// @dev real implementation of this function lies in `ERC721UniqueExtensions`338 #[solidity(hide, rename_selector = "symbol")]339 fn symbol_proxy(&self) -> String {340 self.symbol()341 }342343 /// @notice A distinct Uniform Resource Identifier (URI) for a given asset.344 ///345 /// @dev If the token has a `url` property and it is not empty, it is returned.346 /// 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`.347 /// If the collection property `baseURI` is empty or absent, return "" (empty string)348 /// otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix349 /// otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).350 ///351 /// @return token's const_metadata352 #[solidity(rename_selector = "tokenURI")]353 fn token_uri(&self, token_id: U256) -> Result<String> {354 let token_id_u32: u32 = token_id.try_into().map_err(|_| "token id overflow")?;355356 match get_token_property(self, token_id_u32, &key::url()).as_deref() {357 Err(_) | Ok("") => (),358 Ok(url) => {359 return Ok(url.into());360 }361 };362363 let base_uri =364 pallet_common::Pallet::<T>::get_collection_property(self.id, &key::base_uri())365 .map(BoundedVec::into_inner)366 .map(String::from_utf8)367 .transpose()368 .map_err(|e| {369 Error::Revert(alloc::format!(370 "Can not convert value \"baseURI\" to string with error \"{e}\""371 ))372 })?;373374 let base_uri = match base_uri.as_deref() {375 None | Some("") => {376 return Ok("".into());377 }378 Some(base_uri) => base_uri.into(),379 };380381 Ok(382 match get_token_property(self, token_id_u32, &key::suffix()).as_deref() {383 Err(_) | Ok("") => base_uri,384 Ok(suffix) => base_uri + suffix,385 },386 )387 }388}389390/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension391/// @dev See https://eips.ethereum.org/EIPS/eip-721392#[solidity_interface(name = ERC721Enumerable, expect_selector = 0x780e9d63, enum(derive(PreDispatch)), enum_attr(weight))]393impl<T: Config> NonfungibleHandle<T> {394 /// @notice Enumerate valid NFTs395 /// @param index A counter less than `totalSupply()`396 /// @return The token identifier for the `index`th NFT,397 /// (sort order not specified)398 fn token_by_index(&self, index: U256) -> U256 {399 index400 }401402 /// @dev Not implemented403 fn token_of_owner_by_index(&self, _owner: Address, _index: U256) -> Result<U256> {404 // TODO: Not implemetable405 Err("not implemented".into())406 }407408 /// @notice Count NFTs tracked by this contract409 /// @return A count of valid NFTs tracked by this contract, where each one of410 /// them has an assigned and queryable owner not equal to the zero address411 fn total_supply(&self) -> Result<U256> {412 self.consume_store_reads(1)?;413 Ok(<Pallet<T>>::total_supply(self).into())414 }415}416417/// @title ERC-721 Non-Fungible Token Standard418/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md419#[solidity_interface(name = ERC721, events(ERC721Events), enum(derive(PreDispatch)), enum_attr(weight), expect_selector = 0x80ac58cd)]420impl<T: Config> NonfungibleHandle<T> {421 /// @notice Count all NFTs assigned to an owner422 /// @dev NFTs assigned to the zero address are considered invalid, and this423 /// function throws for queries about the zero address.424 /// @param owner An address for whom to query the balance425 /// @return The number of NFTs owned by `owner`, possibly zero426 fn balance_of(&self, owner: Address) -> Result<U256> {427 self.consume_store_reads(1)?;428 let owner = T::CrossAccountId::from_eth(owner);429 let balance = <AccountBalance<T>>::get((self.id, owner));430 Ok(balance.into())431 }432 /// @notice Find the owner of an NFT433 /// @dev NFTs assigned to zero address are considered invalid, and queries434 /// about them do throw.435 /// @param tokenId The identifier for an NFT436 /// @return The address of the owner of the NFT437 fn owner_of(&self, token_id: U256) -> Result<Address> {438 self.consume_store_reads(1)?;439 let token: TokenId = token_id.try_into()?;440 Ok(*<TokenData<T>>::get((self.id, token))441 .ok_or("token not found")?442 .owner443 .as_eth())444 }445 /// @dev Not implemented446 #[solidity(rename_selector = "safeTransferFrom")]447 fn safe_transfer_from_with_data(448 &mut self,449 _from: Address,450 _to: Address,451 _token_id: U256,452 _data: Bytes,453 ) -> Result<()> {454 // TODO: Not implemetable455 Err("not implemented".into())456 }457 /// @dev Not implemented458 fn safe_transfer_from(&mut self, _from: Address, _to: Address, _token_id: U256) -> Result<()> {459 // TODO: Not implemetable460 Err("not implemented".into())461 }462463 /// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE464 /// TO CONFIRM THAT `to` IS CAPABLE OF RECEIVING NFTS OR ELSE465 /// THEY MAY BE PERMANENTLY LOST466 /// @dev Throws unless `msg.sender` is the current owner or an authorized467 /// operator for this NFT. Throws if `from` is not the current owner. Throws468 /// if `to` is the zero address. Throws if `tokenId` is not a valid NFT.469 /// @param from The current owner of the NFT470 /// @param to The new owner471 /// @param tokenId The NFT to transfer472 #[weight(<CommonWeights<T>>::transfer_from())]473 fn transfer_from(474 &mut self,475 caller: Caller,476 from: Address,477 to: Address,478 token_id: U256,479 ) -> Result<()> {480 let caller = T::CrossAccountId::from_eth(caller);481 let from = T::CrossAccountId::from_eth(from);482 let to = T::CrossAccountId::from_eth(to);483 let token = token_id.try_into()?;484 let budget = self485 .recorder486 .weight_calls_budget(<StructureWeight<T>>::find_parent());487488 <Pallet<T>>::transfer_from(self, &caller, &from, &to, token, &budget)489 .map_err(|e| dispatch_to_evm::<T>(e.error))?;490 Ok(())491 }492493 /// @notice Set or reaffirm the approved address for an NFT494 /// @dev The zero address indicates there is no approved address.495 /// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized496 /// operator of the current owner.497 /// @param approved The new approved NFT controller498 /// @param tokenId The NFT to approve499 #[weight(<SelfWeightOf<T>>::approve())]500 fn approve(&mut self, caller: Caller, approved: Address, token_id: U256) -> Result<()> {501 let caller = T::CrossAccountId::from_eth(caller);502 let approved = T::CrossAccountId::from_eth(approved);503 let token = token_id.try_into()?;504505 <Pallet<T>>::set_allowance(self, &caller, token, Some(&approved))506 .map_err(dispatch_to_evm::<T>)?;507 Ok(())508 }509510 /// @notice Sets or unsets the approval of a given operator.511 /// The `operator` is allowed to transfer all tokens of the `caller` on their behalf.512 /// @param operator Operator513 /// @param approved Should operator status be granted or revoked?514 #[weight(<SelfWeightOf<T>>::set_allowance_for_all())]515 fn set_approval_for_all(516 &mut self,517 caller: Caller,518 operator: Address,519 approved: bool,520 ) -> Result<()> {521 let caller = T::CrossAccountId::from_eth(caller);522 let operator = T::CrossAccountId::from_eth(operator);523524 <Pallet<T>>::set_allowance_for_all(self, &caller, &operator, approved)525 .map_err(dispatch_to_evm::<T>)?;526 Ok(())527 }528529 /// @notice Get the approved address for a single NFT530 /// @dev Throws if `tokenId` is not a valid NFT531 /// @param tokenId The NFT to find the approved address for532 /// @return The approved address for this NFT, or the zero address if there is none533 fn get_approved(&self, token_id: U256) -> Result<Address> {534 let token_id = token_id.try_into()?;535 let operator = <Pallet<T>>::get_allowance(self, token_id).map_err(dispatch_to_evm::<T>)?;536 Ok(if let Some(operator) = operator {537 *operator.as_eth()538 } else {539 Address::zero()540 })541 }542543 /// @notice Tells whether the given `owner` approves the `operator`.544 #[weight(<SelfWeightOf<T>>::allowance_for_all())]545 fn is_approved_for_all(&self, owner: Address, operator: Address) -> Result<bool> {546 let owner = T::CrossAccountId::from_eth(owner);547 let operator = T::CrossAccountId::from_eth(operator);548549 Ok(<Pallet<T>>::allowance_for_all(self, &owner, &operator))550 }551}552553/// @title ERC721 Token that can be irreversibly burned (destroyed).554#[solidity_interface(name = ERC721Burnable, enum(derive(PreDispatch)), enum_attr(weight))]555impl<T: Config> NonfungibleHandle<T> {556 /// @notice Burns a specific ERC721 token.557 /// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized558 /// operator of the current owner.559 /// @param tokenId The NFT to approve560 #[weight(<SelfWeightOf<T>>::burn_item())]561 fn burn(&mut self, caller: Caller, token_id: U256) -> Result<()> {562 let caller = T::CrossAccountId::from_eth(caller);563 let token = token_id.try_into()?;564565 <Pallet<T>>::burn(self, &caller, token).map_err(dispatch_to_evm::<T>)?;566 Ok(())567 }568}569570/// @title ERC721 minting logic.571#[solidity_interface(name = ERC721UniqueMintable, enum(derive(PreDispatch)), enum_attr(weight))]572impl<T: Config> NonfungibleHandle<T> {573 /// @notice Function to mint a token.574 /// @param to The new owner575 /// @return uint256 The id of the newly minted token576 #[weight(<SelfWeightOf<T>>::create_item())]577 fn mint(&mut self, caller: Caller, to: Address) -> Result<U256> {578 let token_id: U256 = <TokensMinted<T>>::get(self.id)579 .checked_add(1)580 .ok_or("item id overflow")?581 .into();582 self.mint_check_id(caller, to, token_id)?;583 Ok(token_id)584 }585586 /// @notice Function to mint a token.587 /// @dev `tokenId` should be obtained with `nextTokenId` method,588 /// unlike standard, you can't specify it manually589 /// @param to The new owner590 /// @param tokenId ID of the minted NFT591 #[solidity(hide, rename_selector = "mint")]592 #[weight(<SelfWeightOf<T>>::create_item())]593 fn mint_check_id(&mut self, caller: Caller, to: Address, token_id: U256) -> Result<bool> {594 let caller = T::CrossAccountId::from_eth(caller);595 let to = T::CrossAccountId::from_eth(to);596 let token_id: u32 = token_id.try_into()?;597 let budget = self598 .recorder599 .weight_calls_budget(<StructureWeight<T>>::find_parent());600601 if <TokensMinted<T>>::get(self.id)602 .checked_add(1)603 .ok_or("item id overflow")?604 != token_id605 {606 return Err("item id should be next".into());607 }608609 <Pallet<T>>::create_item(610 self,611 &caller,612 CreateItemData::<T> {613 properties: BoundedVec::default(),614 owner: to,615 },616 &budget,617 )618 .map_err(dispatch_to_evm::<T>)?;619620 Ok(true)621 }622623 /// @notice Function to mint token with the given tokenUri.624 /// @param to The new owner625 /// @param tokenUri Token URI that would be stored in the NFT properties626 /// @return uint256 The id of the newly minted token627 #[solidity(rename_selector = "mintWithTokenURI")]628 #[weight(<SelfWeightOf<T>>::create_item() + <SelfWeightOf<T>>::set_token_properties(1))]629 fn mint_with_token_uri(630 &mut self,631 caller: Caller,632 to: Address,633 token_uri: String,634 ) -> Result<U256> {635 let token_id: U256 = <TokensMinted<T>>::get(self.id)636 .checked_add(1)637 .ok_or("item id overflow")?638 .into();639 self.mint_with_token_uri_check_id(caller, to, token_id, token_uri)?;640 Ok(token_id)641 }642643 /// @notice Function to mint token with the given tokenUri.644 /// @dev `tokenId` should be obtained with `nextTokenId` method,645 /// unlike standard, you can't specify it manually646 /// @param to The new owner647 /// @param tokenId ID of the minted NFT648 /// @param tokenUri Token URI that would be stored in the NFT properties649 #[solidity(hide, rename_selector = "mintWithTokenURI")]650 #[weight(<SelfWeightOf<T>>::create_item() + <SelfWeightOf<T>>::set_token_properties(1))]651 fn mint_with_token_uri_check_id(652 &mut self,653 caller: Caller,654 to: Address,655 token_id: U256,656 token_uri: String,657 ) -> Result<bool> {658 let key = key::url();659 let permission = get_token_permission::<T>(self.id, &key)?;660 if !permission.collection_admin {661 return Err("Operation is not allowed".into());662 }663664 let caller = T::CrossAccountId::from_eth(caller);665 let to = T::CrossAccountId::from_eth(to);666 let token_id: u32 = token_id.try_into().map_err(|_| "amount overflow")?;667 let budget = self668 .recorder669 .weight_calls_budget(<StructureWeight<T>>::find_parent());670671 if <TokensMinted<T>>::get(self.id)672 .checked_add(1)673 .ok_or("item id overflow")?674 != token_id675 {676 return Err("item id should be next".into());677 }678679 let mut properties = CollectionPropertiesVec::default();680 properties681 .try_push(Property {682 key,683 value: token_uri684 .into_bytes()685 .try_into()686 .map_err(|_| "token uri is too long")?,687 })688 .map_err(|e| Error::Revert(alloc::format!("Can't add property: {e:?}")))?;689690 <Pallet<T>>::create_item(691 self,692 &caller,693 CreateItemData::<T> {694 properties,695 owner: to,696 },697 &budget,698 )699 .map_err(dispatch_to_evm::<T>)?;700 Ok(true)701 }702}703704fn get_token_property<T: Config>(705 collection: &CollectionHandle<T>,706 token_id: u32,707 key: &up_data_structs::PropertyKey,708) -> Result<String> {709 collection.consume_store_reads(1)?;710 let properties = <TokenProperties<T>>::try_get((collection.id, token_id))711 .map_err(|_| Error::Revert("Token properties not found".into()))?;712 if let Some(property) = properties.get(key) {713 return Ok(String::from_utf8_lossy(property).into());714 }715716 Err("Property tokenURI not found".into())717}718719fn get_token_permission<T: Config>(720 collection_id: CollectionId,721 key: &PropertyKey,722) -> Result<PropertyPermission> {723 let token_property_permissions = CollectionPropertyPermissions::<T>::try_get(collection_id)724 .map_err(|_| Error::Revert("No permissions for collection".into()))?;725 let a = token_property_permissions726 .get(key)727 .map(Clone::clone)728 .ok_or_else(|| {729 let key = String::from_utf8(key.clone().into_inner()).unwrap_or_default();730 Error::Revert(alloc::format!("No permission for key {key}"))731 })?;732 Ok(a)733}734735/// @title Unique extensions for ERC721.736#[solidity_interface(name = ERC721UniqueExtensions, enum(derive(PreDispatch)), enum_attr(weight))]737impl<T: Config> NonfungibleHandle<T>738where739 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,740{741 /// @notice A descriptive name for a collection of NFTs in this contract742 fn name(&self) -> String {743 decode_utf16(self.name.iter().copied())744 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))745 .collect::<String>()746 }747748 /// @notice An abbreviated name for NFTs in this contract749 fn symbol(&self) -> String {750 String::from_utf8_lossy(&self.token_prefix).into()751 }752753 /// @notice A description for the collection.754 fn description(&self) -> String {755 decode_utf16(self.description.iter().copied())756 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))757 .collect::<String>()758 }759760 /// Returns the owner (in cross format) of the token.761 ///762 /// @param tokenId Id for the token.763 #[solidity(hide)]764 fn cross_owner_of(&self, token_id: U256) -> Result<eth::CrossAddress> {765 Self::owner_of_cross(self, token_id)766 }767768 /// Returns the owner (in cross format) of the token.769 ///770 /// @param tokenId Id for the token.771 fn owner_of_cross(&self, token_id: U256) -> Result<eth::CrossAddress> {772 Self::token_owner(self, token_id.try_into()?)773 .map(|o| eth::CrossAddress::from_sub_cross_account::<T>(&o))774 .map_err(|_| Error::Revert("token not found".into()))775 }776777 /// @notice Count all NFTs assigned to an owner778 /// @param owner An cross address for whom to query the balance779 /// @return The number of NFTs owned by `owner`, possibly zero780 fn balance_of_cross(&self, owner: eth::CrossAddress) -> Result<U256> {781 self.consume_store_reads(1)?;782 let balance = <AccountBalance<T>>::get((self.id, owner.into_sub_cross_account::<T>()?));783 Ok(balance.into())784 }785786 /// Returns the token properties.787 ///788 /// @param tokenId Id for the token.789 /// @param keys Properties keys. Empty keys for all propertyes.790 /// @return Vector of properties key/value pairs.791 fn properties(&self, token_id: U256, keys: Vec<String>) -> Result<Vec<eth::Property>> {792 let keys = keys793 .into_iter()794 .map(|key| {795 <Vec<u8>>::from(key)796 .try_into()797 .map_err(|_| Error::Revert("key too large".into()))798 })799 .collect::<Result<Vec<_>>>()?;800801 <Self as CommonCollectionOperations<T>>::token_properties(802 self,803 token_id.try_into()?,804 if keys.is_empty() { None } else { Some(keys) },805 )806 .into_iter()807 .map(eth::Property::try_from)808 .collect::<Result<Vec<_>>>()809 }810811 /// @notice Set or reaffirm the approved address for an NFT812 /// @dev The zero address indicates there is no approved address.813 /// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized814 /// operator of the current owner.815 /// @param approved The new substrate address approved NFT controller816 /// @param tokenId The NFT to approve817 #[weight(<SelfWeightOf<T>>::approve())]818 fn approve_cross(819 &mut self,820 caller: Caller,821 approved: eth::CrossAddress,822 token_id: U256,823 ) -> Result<()> {824 let caller = T::CrossAccountId::from_eth(caller);825 let approved = approved.into_sub_cross_account::<T>()?;826 let token = token_id.try_into()?;827828 <Pallet<T>>::set_allowance(self, &caller, token, Some(&approved))829 .map_err(dispatch_to_evm::<T>)?;830 Ok(())831 }832833 /// @notice Transfer ownership of an NFT834 /// @dev Throws unless `msg.sender` is the current owner. Throws if `to`835 /// is the zero address. Throws if `tokenId` is not a valid NFT.836 /// @param to The new owner837 /// @param tokenId The NFT to transfer838 #[weight(<CommonWeights<T>>::transfer())]839 fn transfer(&mut self, caller: Caller, to: Address, token_id: U256) -> Result<()> {840 let caller = T::CrossAccountId::from_eth(caller);841 let to = T::CrossAccountId::from_eth(to);842 let token = token_id.try_into()?;843 let budget = self844 .recorder845 .weight_calls_budget(<StructureWeight<T>>::find_parent());846847 <Pallet<T>>::transfer(self, &caller, &to, token, &budget)848 .map_err(|e| dispatch_to_evm::<T>(e.error))?;849 Ok(())850 }851852 /// @notice Transfer ownership of an NFT853 /// @dev Throws unless `msg.sender` is the current owner. Throws if `to`854 /// is the zero address. Throws if `tokenId` is not a valid NFT.855 /// @param to The new owner856 /// @param tokenId The NFT to transfer857 #[weight(<CommonWeights<T>>::transfer())]858 fn transfer_cross(859 &mut self,860 caller: Caller,861 to: eth::CrossAddress,862 token_id: U256,863 ) -> Result<()> {864 let caller = T::CrossAccountId::from_eth(caller);865 let to = to.into_sub_cross_account::<T>()?;866 let token = token_id.try_into()?;867 let budget = self868 .recorder869 .weight_calls_budget(<StructureWeight<T>>::find_parent());870871 <Pallet<T>>::transfer(self, &caller, &to, token, &budget)872 .map_err(|e| dispatch_to_evm::<T>(e.error))?;873 Ok(())874 }875876 /// @notice Transfer ownership of an NFT from cross account address to cross account address877 /// @dev Throws unless `msg.sender` is the current owner. Throws if `to`878 /// is the zero address. Throws if `tokenId` is not a valid NFT.879 /// @param from Cross acccount address of current owner880 /// @param to Cross acccount address of new owner881 /// @param tokenId The NFT to transfer882 #[weight(<CommonWeights<T>>::transfer_from())]883 fn transfer_from_cross(884 &mut self,885 caller: Caller,886 from: eth::CrossAddress,887 to: eth::CrossAddress,888 token_id: U256,889 ) -> Result<()> {890 let caller = T::CrossAccountId::from_eth(caller);891 let from = from.into_sub_cross_account::<T>()?;892 let to = to.into_sub_cross_account::<T>()?;893 let token_id = token_id.try_into()?;894 let budget = self895 .recorder896 .weight_calls_budget(<StructureWeight<T>>::find_parent());897 Pallet::<T>::transfer_from(self, &caller, &from, &to, token_id, &budget)898 .map_err(|e| dispatch_to_evm::<T>(e.error))?;899 Ok(())900 }901902 /// @notice Burns a specific ERC721 token.903 /// @dev Throws unless `msg.sender` is the current owner or an authorized904 /// operator for this NFT. Throws if `from` is not the current owner. Throws905 /// if `to` is the zero address. Throws if `tokenId` is not a valid NFT.906 /// @param from The current owner of the NFT907 /// @param tokenId The NFT to transfer908 #[solidity(hide)]909 #[weight(<SelfWeightOf<T>>::burn_from())]910 fn burn_from(&mut self, caller: Caller, from: Address, token_id: U256) -> Result<()> {911 let caller = T::CrossAccountId::from_eth(caller);912 let from = T::CrossAccountId::from_eth(from);913 let token = token_id.try_into()?;914 let budget = self915 .recorder916 .weight_calls_budget(<StructureWeight<T>>::find_parent());917918 <Pallet<T>>::burn_from(self, &caller, &from, token, &budget)919 .map_err(dispatch_to_evm::<T>)?;920 Ok(())921 }922923 /// @notice Burns a specific ERC721 token.924 /// @dev Throws unless `msg.sender` is the current owner or an authorized925 /// operator for this NFT. Throws if `from` is not the current owner. Throws926 /// if `to` is the zero address. Throws if `tokenId` is not a valid NFT.927 /// @param from The current owner of the NFT928 /// @param tokenId The NFT to transfer929 #[weight(<SelfWeightOf<T>>::burn_from())]930 fn burn_from_cross(931 &mut self,932 caller: Caller,933 from: eth::CrossAddress,934 token_id: U256,935 ) -> Result<()> {936 let caller = T::CrossAccountId::from_eth(caller);937 let from = from.into_sub_cross_account::<T>()?;938 let token = token_id.try_into()?;939 let budget = self940 .recorder941 .weight_calls_budget(<StructureWeight<T>>::find_parent());942943 <Pallet<T>>::burn_from(self, &caller, &from, token, &budget)944 .map_err(dispatch_to_evm::<T>)?;945 Ok(())946 }947948 /// @notice Returns next free NFT ID.949 fn next_token_id(&self) -> Result<U256> {950 self.consume_store_reads(1)?;951 Ok(<Pallet<T>>::next_token_id(self)952 .map_err(dispatch_to_evm::<T>)?953 .into())954 }955956 /// @notice Function to mint multiple tokens.957 /// @dev `tokenIds` should be an array of consecutive numbers and first number958 /// should be obtained with `nextTokenId` method959 /// @param to The new owner960 /// @param tokenIds IDs of the minted NFTs961 #[solidity(hide)]962 #[weight(<SelfWeightOf<T>>::create_multiple_items(token_ids.len() as u32))]963 fn mint_bulk(&mut self, caller: Caller, to: Address, token_ids: Vec<U256>) -> Result<bool> {964 let caller = T::CrossAccountId::from_eth(caller);965 let to = T::CrossAccountId::from_eth(to);966 let mut expected_index = <TokensMinted<T>>::get(self.id)967 .checked_add(1)968 .ok_or("item id overflow")?;969 let budget = self970 .recorder971 .weight_calls_budget(<StructureWeight<T>>::find_parent());972973 let total_tokens = token_ids.len();974 for id in token_ids.into_iter() {975 let id: u32 = id.try_into().map_err(|_| "token id overflow")?;976 if id != expected_index {977 return Err("item id should be next".into());978 }979 expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;980 }981 let data = (0..total_tokens)982 .map(|_| CreateItemData::<T> {983 properties: BoundedVec::default(),984 owner: to.clone(),985 })986 .collect();987988 <Pallet<T>>::create_multiple_items(self, &caller, data, &budget)989 .map_err(dispatch_to_evm::<T>)?;990 Ok(true)991 }992993 /// @notice Function to mint a token.994 /// @param data Array of pairs of token owner and token's properties for minted token995 #[weight(<SelfWeightOf<T>>::create_multiple_items(data.len() as u32) + <SelfWeightOf<T>>::set_token_properties(data.len() as u32))]996 fn mint_bulk_cross(&mut self, caller: Caller, data: Vec<MintTokenData>) -> Result<bool> {997 let caller = T::CrossAccountId::from_eth(caller);998 let budget = self999 .recorder1000 .weight_calls_budget(<StructureWeight<T>>::find_parent());10011002 let mut create_nft_data = Vec::with_capacity(data.len());1003 for MintTokenData { owner, properties } in data {1004 let owner = owner.into_sub_cross_account::<T>()?;1005 create_nft_data.push(CreateItemData::<T> {1006 properties: properties1007 .into_iter()1008 .map(|property| property.try_into())1009 .collect::<Result<Vec<_>>>()?1010 .try_into()1011 .map_err(|_| "too many properties")?,1012 owner,1013 });1014 }10151016 <Pallet<T>>::create_multiple_items(self, &caller, create_nft_data, &budget)1017 .map_err(dispatch_to_evm::<T>)?;1018 Ok(true)1019 }10201021 /// @notice Function to mint multiple tokens with the given tokenUris.1022 /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive1023 /// numbers and first number should be obtained with `nextTokenId` method1024 /// @param to The new owner1025 /// @param tokens array of pairs of token ID and token URI for minted tokens1026 #[solidity(hide, rename_selector = "mintBulkWithTokenURI")]1027 #[weight(<SelfWeightOf<T>>::create_multiple_items(tokens.len() as u32) + <SelfWeightOf<T>>::set_token_properties(tokens.len() as u32))]1028 fn mint_bulk_with_token_uri(1029 &mut self,1030 caller: Caller,1031 to: Address,1032 tokens: Vec<TokenUri>,1033 ) -> Result<bool> {1034 let key = key::url();1035 let caller = T::CrossAccountId::from_eth(caller);1036 let to = T::CrossAccountId::from_eth(to);1037 let mut expected_index = <TokensMinted<T>>::get(self.id)1038 .checked_add(1)1039 .ok_or("item id overflow")?;1040 let budget = self1041 .recorder1042 .weight_calls_budget(<StructureWeight<T>>::find_parent());10431044 let mut data = Vec::with_capacity(tokens.len());1045 for TokenUri { id, uri } in tokens {1046 let id: u32 = id.try_into().map_err(|_| "token id overflow")?;1047 if id != expected_index {1048 return Err("item id should be next".into());1049 }1050 expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;10511052 let mut properties = CollectionPropertiesVec::default();1053 properties1054 .try_push(Property {1055 key: key.clone(),1056 value: uri1057 .into_bytes()1058 .try_into()1059 .map_err(|_| "token uri is too long")?,1060 })1061 .map_err(|e| Error::Revert(alloc::format!("Can't add property: {e:?}")))?;10621063 data.push(CreateItemData::<T> {1064 properties,1065 owner: to.clone(),1066 });1067 }10681069 <Pallet<T>>::create_multiple_items(self, &caller, data, &budget)1070 .map_err(dispatch_to_evm::<T>)?;1071 Ok(true)1072 }10731074 /// @notice Function to mint a token.1075 /// @param to The new owner crossAccountId1076 /// @param properties Properties of minted token1077 /// @return uint256 The id of the newly minted token1078 #[weight(<SelfWeightOf<T>>::create_item() + <SelfWeightOf<T>>::set_token_properties(properties.len() as u32))]1079 fn mint_cross(1080 &mut self,1081 caller: Caller,1082 to: eth::CrossAddress,1083 properties: Vec<eth::Property>,1084 ) -> Result<U256> {1085 let token_id = <TokensMinted<T>>::get(self.id)1086 .checked_add(1)1087 .ok_or("item id overflow")?;10881089 let to = to.into_sub_cross_account::<T>()?;10901091 let properties = properties1092 .into_iter()1093 .map(eth::Property::try_into)1094 .collect::<Result<Vec<_>>>()?1095 .try_into()1096 .map_err(|_| Error::Revert("too many properties".to_string()))?;10971098 let caller = T::CrossAccountId::from_eth(caller);10991100 let budget = self1101 .recorder1102 .weight_calls_budget(<StructureWeight<T>>::find_parent());11031104 <Pallet<T>>::create_item(1105 self,1106 &caller,1107 CreateItemData::<T> {1108 properties,1109 owner: to,1110 },1111 &budget,1112 )1113 .map_err(dispatch_to_evm::<T>)?;11141115 Ok(token_id.into())1116 }11171118 /// @notice Returns collection helper contract address1119 fn collection_helper_address(&self) -> Address {1120 T::ContractAddress::get()1121 }1122}11231124#[solidity_interface(1125 name = UniqueNFT,1126 is(1127 ERC721,1128 ERC721Enumerable,1129 ERC721UniqueExtensions,1130 ERC721UniqueMintable,1131 ERC721Burnable,1132 ERC721Metadata(if(this.flags.erc721metadata)),1133 Collection(via(common_mut returns CollectionHandle<T>)),1134 TokenProperties,1135 ),1136 enum(derive(PreDispatch)),1137)]1138impl<T: Config> NonfungibleHandle<T> where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]> {}11391140// Not a tests, but code generators1141generate_stubgen!(gen_impl, UniqueNFTCall<()>, true);1142generate_stubgen!(gen_iface, UniqueNFTCall<()>, false);11431144impl<T: Config> CommonEvmHandler for NonfungibleHandle<T>1145where1146 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,1147{1148 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueNFT.raw");11491150 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {1151 call::<T, UniqueNFTCall<T>, _, _>(handle, self)1152 }1153}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(())