difftreelog
fix EVM mint with properties, minor improvements
in: master
9 files changed
pallets/common/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/common/src/benchmarking.rs
+++ b/pallets/common/src/benchmarking.rs
@@ -29,9 +29,8 @@
use sp_std::{vec, vec::Vec};
use up_data_structs::{
AccessMode, CollectionId, CollectionMode, CollectionPermissions, CreateCollectionData,
- NestingPermissions, PropertiesPermissionMap, Property, PropertyKey, PropertyValue,
- MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_COLLECTION_NAME_LENGTH, MAX_PROPERTIES_PER_ITEM,
- MAX_TOKEN_PREFIX_LENGTH,
+ NestingPermissions, Property, PropertyKey, PropertyValue, MAX_COLLECTION_DESCRIPTION_LENGTH,
+ MAX_COLLECTION_NAME_LENGTH, MAX_PROPERTIES_PER_ITEM, MAX_TOKEN_PREFIX_LENGTH,
};
use crate::{BenchmarkPropertyWriter, CollectionHandle, Config, Pallet};
@@ -190,31 +189,6 @@
#[block]
{
<Pallet<T>>::set_collection_properties(&collection, &owner, props.into_iter())?;
- }
-
- Ok(())
- }
-
- #[benchmark]
- fn delete_collection_properties(
- b: Linear<0, MAX_PROPERTIES_PER_ITEM>,
- ) -> Result<(), BenchmarkError> {
- bench_init! {
- owner: sub; collection: collection(owner);
- owner: cross_from_sub;
- };
- let props = (0..b)
- .map(|p| Property {
- key: property_key(p as usize),
- value: property_value(),
- })
- .collect::<Vec<_>>();
- <Pallet<T>>::set_collection_properties(&collection, &owner, props.into_iter())?;
- let to_delete = (0..b).map(|p| property_key(p as usize)).collect::<Vec<_>>();
-
- #[block]
- {
- <Pallet<T>>::delete_collection_properties(&collection, &owner, to_delete.into_iter())?;
}
Ok(())
@@ -253,7 +227,7 @@
}
#[benchmark]
- fn init_token_properties_common() -> Result<(), BenchmarkError> {
+ fn property_writer_load_collection_info() -> Result<(), BenchmarkError> {
bench_init! {
owner: sub; collection: collection(owner);
sender: sub;
pallets/common/src/erc.rsdiffbeforeafterboth--- a/pallets/common/src/erc.rs
+++ b/pallets/common/src/erc.rs
@@ -126,7 +126,7 @@
///
/// @param key Property key.
#[solidity(hide)]
- #[weight(<SelfWeightOf<T>>::delete_collection_properties(1))]
+ #[weight(<SelfWeightOf<T>>::set_collection_properties(1))]
fn delete_collection_property(&mut self, caller: Caller, key: String) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
let key = <Vec<u8>>::from(key)
@@ -139,7 +139,7 @@
/// Delete collection properties.
///
/// @param keys Properties keys.
- #[weight(<SelfWeightOf<T>>::delete_collection_properties(keys.len() as u32))]
+ #[weight(<SelfWeightOf<T>>::set_collection_properties(keys.len() as u32))]
fn delete_collection_properties(&mut self, caller: Caller, keys: Vec<String>) -> Result<()> {
let caller = T::CrossAccountId::from_eth(caller);
let keys = keys
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -2626,8 +2626,8 @@
impl<T: Config> BenchmarkPropertyWriter<T> {
/// Creates a [`PropertyWriter`] for benchmarking tokens properties writing.
pub fn new<'a, Handle>(
- collection: &Handle,
- collection_lazy_info: PropertyWriterLazyCollectionInfo,
+ collection: &'a Handle,
+ collection_lazy_info: PropertyWriterLazyCollectionInfo<'a>,
) -> PropertyWriter<'a, Self, T, Handle>
where
Handle: CommonCollectionOperations<T> + Deref<Target = CollectionHandle<T>>,
pallets/nonfungible/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/benchmarking.rs
+++ b/pallets/nonfungible/src/benchmarking.rs
@@ -18,7 +18,6 @@
use pallet_common::{
bench_init,
benchmarking::{create_collection_raw, property_key, property_value},
- CommonCollectionOperations,
};
use sp_std::prelude::*;
use up_data_structs::{
@@ -131,53 +130,12 @@
#[block]
{
<Pallet<T>>::burn(&collection, &burner, item)?;
- }
-
- Ok(())
- }
-
- #[benchmark]
- fn burn_recursively_self_raw() -> Result<(), BenchmarkError> {
- bench_init! {
- owner: sub; collection: collection(owner);
- sender: cross_from_sub(owner); burner: cross_sub;
- };
- let item = create_max_item(&collection, &sender, burner.clone())?;
-
- #[block]
- {
- <Pallet<T>>::burn_recursively(&collection, &burner, item, &Unlimited, &Unlimited)?;
}
Ok(())
}
#[benchmark]
- fn burn_recursively_breadth_plus_self_plus_self_per_each_raw(
- b: Linear<0, 200>,
- ) -> Result<(), BenchmarkError> {
- bench_init! {
- owner: sub; collection: collection(owner);
- sender: cross_from_sub(owner); burner: cross_sub;
- };
- let item = create_max_item(&collection, &sender, burner.clone())?;
- for _ in 0..b {
- create_max_item(
- &collection,
- &sender,
- T::CrossTokenAddressMapping::token_to_address(collection.id, item),
- )?;
- }
-
- #[block]
- {
- <Pallet<T>>::burn_recursively(&collection, &burner, item, &Unlimited, &Unlimited)?;
- }
-
- Ok(())
- }
-
- #[benchmark]
fn transfer_raw() -> Result<(), BenchmarkError> {
bench_init! {
owner: sub; collection: collection(owner);
@@ -262,116 +220,34 @@
{
<Pallet<T>>::burn_from(&collection, &burner, &sender, item, &Unlimited)?;
}
- }
- // set_token_properties {
- // let b in 0..MAX_PROPERTIES_PER_ITEM;
- // bench_init!{
- // owner: sub; collection: collection(owner);
- // owner: cross_from_sub;
- // };
- // let perms = (0..b).map(|k| PropertyKeyPermission {
- // key: property_key(k as usize),
- // permission: PropertyPermission {
- // mutable: false,
- // collection_admin: true,
- // token_owner: true,
- // },
- // }).collect::<Vec<_>>();
- // <Pallet<T>>::set_token_property_permissions(&collection, &owner, perms)?;
- // let props = (0..b).map(|k| Property {
- // key: property_key(k as usize),
- // value: property_value(),
- // }).collect::<Vec<_>>();
- // let item = create_max_item(&collection, &owner, owner.clone())?;
- // }: {<Pallet<T>>::set_token_properties(&collection, &owner, item, props.into_iter(), &Unlimited)?}
-
- // load_token_properties {
- // bench_init!{
- // owner: sub; collection: collection(owner);
- // owner: cross_from_sub;
- // };
-
- // let item = create_max_item(&collection, &owner, owner.clone())?;
- // }: {
- // pallet_common::BenchmarkPropertyWriter::<T>::load_token_properties(
- // &collection,
- // item,
- // )
- // }
-
- // write_token_properties {
- // let b in 0..MAX_PROPERTIES_PER_ITEM;
- // bench_init!{
- // owner: sub; collection: collection(owner);
- // owner: cross_from_sub;
- // };
+ Ok(())
+ }
- // let perms = (0..b).map(|k| PropertyKeyPermission {
- // key: property_key(k as usize),
- // permission: PropertyPermission {
- // mutable: false,
- // collection_admin: true,
- // token_owner: true,
- // },
- // }).collect::<Vec<_>>();
- // <Pallet<T>>::set_token_property_permissions(&collection, &owner, perms)?;
- // let props = (0..b).map(|k| Property {
- // key: property_key(k as usize),
- // value: property_value(),
- // }).collect::<Vec<_>>();
- // let item = create_max_item(&collection, &owner, owner.clone())?;
-
- // let lazy_collection_info = pallet_common::BenchmarkPropertyWriter::<T>::load_collection_info(
- // &collection,
- // &owner,
- // );
- // }: {
- // let mut property_writer = pallet_common::BenchmarkPropertyWriter::new(&collection, lazy_collection_info);
-
- // property_writer.write_token_properties(
- // item,
- // props.into_iter(),
- // crate::erc::ERC721TokenEvent::TokenChanged {
- // token_id: item.into(),
- // }
- // .to_log(T::ContractAddress::get()),
- // )?
- // }
-
#[benchmark]
- fn set_token_property_permissions(
- b: Linear<0, MAX_PROPERTIES_PER_ITEM>,
- ) -> Result<(), BenchmarkError> {
+ fn load_token_properties() -> Result<(), BenchmarkError> {
bench_init! {
owner: sub; collection: collection(owner);
owner: cross_from_sub;
};
- let perms = (0..b)
- .map(|k| PropertyKeyPermission {
- key: property_key(k as usize),
- permission: PropertyPermission {
- mutable: false,
- collection_admin: false,
- token_owner: false,
- },
- })
- .collect::<Vec<_>>();
+ let item = create_max_item(&collection, &owner, owner.clone())?;
+
#[block]
{
- <Pallet<T>>::set_token_property_permissions(&collection, &owner, perms)?;
+ pallet_common::BenchmarkPropertyWriter::<T>::load_token_properties(&collection, item);
}
Ok(())
}
#[benchmark]
- fn set_token_properties(b: Linear<0, MAX_PROPERTIES_PER_ITEM>) -> Result<(), BenchmarkError> {
+ fn write_token_properties(b: Linear<0, MAX_PROPERTIES_PER_ITEM>) -> Result<(), BenchmarkError> {
bench_init! {
owner: sub; collection: collection(owner);
owner: cross_from_sub;
};
+
let perms = (0..b)
.map(|k| PropertyKeyPermission {
key: property_key(k as usize),
@@ -391,71 +267,29 @@
.collect::<Vec<_>>();
let item = create_max_item(&collection, &owner, owner.clone())?;
+ let lazy_collection_info =
+ pallet_common::BenchmarkPropertyWriter::<T>::load_collection_info(&collection, &owner);
+
#[block]
{
- <Pallet<T>>::set_token_properties(
- &collection,
- &owner,
+ let mut property_writer =
+ pallet_common::BenchmarkPropertyWriter::new(&collection, lazy_collection_info);
+
+ property_writer.write_token_properties(
item,
props.into_iter(),
- &Unlimited,
+ crate::erc::ERC721TokenEvent::TokenChanged {
+ token_id: item.into(),
+ }
+ .to_log(T::ContractAddress::get()),
)?;
}
Ok(())
}
- // TODO:
#[benchmark]
- fn init_token_properties(b: Linear<0, MAX_PROPERTIES_PER_ITEM>) -> Result<(), BenchmarkError> {
- // bench_init! {
- // owner: sub; collection: collection(owner);
- // owner: cross_from_sub;
- // };
-
- // let perms = (0..b)
- // .map(|k| PropertyKeyPermission {
- // key: property_key(k as usize),
- // permission: PropertyPermission {
- // mutable: false,
- // collection_admin: true,
- // token_owner: true,
- // },
- // })
- // .collect::<Vec<_>>();
- // <Pallet<T>>::set_token_property_permissions(&collection, &owner, perms)?;
- #[block]
- {}
- // let props = (0..b)
- // .map(|k| Property {
- // key: property_key(k as usize),
- // value: property_value(),
- // })
- // .collect::<Vec<_>>();
- // let item = create_max_item(&collection, &owner, owner.clone())?;
-
- // let (is_collection_admin, property_permissions) =
- // load_is_admin_and_property_permissions(&collection, &owner);
- // #[block]
- // {
- // let mut property_writer =
- // pallet_common::BenchmarkPropertyWriter::new(&collection, lazy_collection_info);
-
- // property_writer.write_token_properties(
- // item,
- // props.into_iter(),
- // crate::erc::ERC721TokenEvent::TokenChanged {
- // token_id: item.into(),
- // }
- // .to_log(T::ContractAddress::get()),
- // )?;
- // }
-
- Ok(())
- }
-
- #[benchmark]
- fn delete_token_properties(
+ fn set_token_property_permissions(
b: Linear<0, MAX_PROPERTIES_PER_ITEM>,
) -> Result<(), BenchmarkError> {
bench_init! {
@@ -466,54 +300,16 @@
.map(|k| PropertyKeyPermission {
key: property_key(k as usize),
permission: PropertyPermission {
- mutable: true,
- collection_admin: true,
- token_owner: true,
+ mutable: false,
+ collection_admin: false,
+ token_owner: false,
},
- })
- .collect::<Vec<_>>();
- <Pallet<T>>::set_token_property_permissions(&collection, &owner, perms)?;
- let props = (0..b)
- .map(|k| Property {
- key: property_key(k as usize),
- value: property_value(),
})
.collect::<Vec<_>>();
- let item = create_max_item(&collection, &owner, owner.clone())?;
- <Pallet<T>>::set_token_properties(
- &collection,
- &owner,
- item,
- props.into_iter(),
- &Unlimited,
- )?;
- let to_delete = (0..b).map(|k| property_key(k as usize)).collect::<Vec<_>>();
#[block]
{
- <Pallet<T>>::delete_token_properties(
- &collection,
- &owner,
- item,
- to_delete.into_iter(),
- &Unlimited,
- )?;
- }
-
- Ok(())
- }
-
- #[benchmark]
- fn token_owner() -> Result<(), BenchmarkError> {
- bench_init! {
- owner: sub; collection: collection(owner);
- owner: cross_from_sub;
- };
- let item = create_max_item(&collection, &owner, owner.clone())?;
-
- #[block]
- {
- collection.token_owner(item).unwrap();
+ <Pallet<T>>::set_token_property_permissions(&collection, &owner, perms)?;
}
Ok(())
pallets/nonfungible/src/common.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/common.rs
+++ b/pallets/nonfungible/src/common.rs
@@ -39,24 +39,21 @@
impl<T: Config> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T> {
fn create_multiple_items_ex(data: &CreateItemExData<T::CrossAccountId>) -> Weight {
match data {
- CreateItemExData::NFT(t) => <SelfWeightOf<T>>::create_multiple_items_ex(t.len() as u32)
- .saturating_add(write_token_properties_total_weight::<T, _>(
- t.iter().map(|t| t.properties.len() as u32),
- <SelfWeightOf<T>>::write_token_properties,
- )),
+ CreateItemExData::NFT(t) => mint_with_props_weight::<T>(
+ <SelfWeightOf<T>>::create_multiple_items_ex(t.len() as u32),
+ t.iter().map(|t| t.properties.len() as u32),
+ ),
_ => Weight::zero(),
}
}
fn create_multiple_items(data: &[up_data_structs::CreateItemData]) -> Weight {
- <SelfWeightOf<T>>::create_multiple_items(data.len() as u32).saturating_add(
- write_token_properties_total_weight::<T, _>(
- data.iter().map(|t| match t {
- up_data_structs::CreateItemData::NFT(n) => n.properties.len() as u32,
- _ => 0,
- }),
- <SelfWeightOf<T>>::write_token_properties,
- ),
+ mint_with_props_weight::<T>(
+ <SelfWeightOf<T>>::create_multiple_items(data.len() as u32),
+ data.iter().map(|t| match t {
+ up_data_structs::CreateItemData::NFT(n) => n.properties.len() as u32,
+ _ => 0,
+ }),
)
}
@@ -113,6 +110,16 @@
}
}
+pub(crate) fn mint_with_props_weight<T: Config>(
+ create_no_data_weight: Weight,
+ tokens: impl Iterator<Item = u32> + Clone,
+) -> Weight {
+ create_no_data_weight.saturating_add(write_token_properties_total_weight::<T, _>(
+ tokens,
+ <SelfWeightOf<T>>::write_token_properties,
+ ))
+}
+
fn map_create_data<T: Config>(
data: up_data_structs::CreateItemData,
to: &T::CrossAccountId,
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::{decode_utf16, REPLACEMENT_CHARACTER},27 convert::TryInto,28};2930use evm_coder::{abi::AbiType, generate_stubgen, solidity_interface, types::*, AbiCoder, ToLog};31use frame_support::BoundedVec;32use pallet_common::{33 erc::{static_property::key, CollectionCall, CommonEvmHandler, PrecompileResult},34 eth::{self, TokenUri},35 CollectionHandle, CollectionPropertyPermissions, CommonCollectionOperations, CommonWeightInfo,36};37use pallet_evm::{account::CrossAccountId, PrecompileHandle};38use pallet_evm_coder_substrate::{39 call, dispatch_to_evm,40 execution::{Error, PreDispatch, Result},41 frontier_contract, SubstrateRecorder,42};43use pallet_structure::{weights::WeightInfo as _, SelfWeightOf as StructureWeight};44use sp_core::{Get, U256};45use sp_std::{vec, vec::Vec};46use up_data_structs::{47 budget::Budget, CollectionId, CollectionPropertiesVec, Property, PropertyKey,48 PropertyKeyPermission, PropertyPermission, TokenId,49};5051use crate::{52 common::CommonWeights, weights::WeightInfo, AccountBalance, Config, CreateItemData,53 NonfungibleHandle, Pallet, SelfWeightOf, TokenData, TokenProperties, TokensMinted,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}8081fn nesting_budget<T: Config>(recorder: &SubstrateRecorder<T>) -> impl Budget + '_ {82 recorder.weight_calls_budget(<StructureWeight<T>>::find_parent())83}8485/// @title A contract that allows to set and delete token properties and change token property permissions.86#[solidity_interface(name = TokenProperties, events(ERC721TokenEvent), enum(derive(PreDispatch)), enum_attr(weight))]87impl<T: Config> NonfungibleHandle<T> {88 /// @notice Set permissions for token property.89 /// @dev Throws error if `msg.sender` is not admin or owner of the collection.90 /// @param key Property key.91 /// @param isMutable Permission to mutate property.92 /// @param collectionAdmin Permission to mutate property by collection admin if property is mutable.93 /// @param tokenOwner Permission to mutate property by token owner if property is mutable.94 #[solidity(hide)]95 #[weight(<SelfWeightOf<T>>::set_token_property_permissions(1))]96 fn set_token_property_permission(97 &mut self,98 caller: Caller,99 key: String,100 is_mutable: bool,101 collection_admin: bool,102 token_owner: bool,103 ) -> Result<()> {104 let caller = T::CrossAccountId::from_eth(caller);105 <Pallet<T>>::set_token_property_permissions(106 self,107 &caller,108 vec![PropertyKeyPermission {109 key: <Vec<u8>>::from(key)110 .try_into()111 .map_err(|_| "too long key")?,112 permission: PropertyPermission {113 mutable: is_mutable,114 collection_admin,115 token_owner,116 },117 }],118 )119 .map_err(dispatch_to_evm::<T>)120 }121122 /// @notice Set permissions for token property.123 /// @dev Throws error if `msg.sender` is not admin or owner of the collection.124 /// @param permissions Permissions for keys.125 #[weight(<SelfWeightOf<T>>::set_token_property_permissions(permissions.len() as u32))]126 fn set_token_property_permissions(127 &mut self,128 caller: Caller,129 permissions: Vec<eth::TokenPropertyPermission>,130 ) -> Result<()> {131 let caller = T::CrossAccountId::from_eth(caller);132 let perms = eth::TokenPropertyPermission::into_property_key_permissions(permissions)?;133134 <Pallet<T>>::set_token_property_permissions(self, &caller, perms)135 .map_err(dispatch_to_evm::<T>)136 }137138 /// @notice Get permissions for token properties.139 fn token_property_permissions(&self) -> Result<Vec<eth::TokenPropertyPermission>> {140 let perms = <Pallet<T>>::token_property_permission(self.id);141 Ok(perms142 .into_iter()143 .map(eth::TokenPropertyPermission::from)144 .collect())145 }146147 /// @notice Set token property value.148 /// @dev Throws error if `msg.sender` has no permission to edit the property.149 /// @param tokenId ID of the token.150 /// @param key Property key.151 /// @param value Property value.152 #[solidity(hide)]153 #[weight(<CommonWeights<T>>::set_token_properties(1))]154 fn set_property(155 &mut self,156 caller: Caller,157 token_id: U256,158 key: String,159 value: Bytes,160 ) -> Result<()> {161 let caller = T::CrossAccountId::from_eth(caller);162 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;163 let key = <Vec<u8>>::from(key)164 .try_into()165 .map_err(|_| "key too long")?;166 let value = value.0.try_into().map_err(|_| "value too long")?;167168 <Pallet<T>>::set_token_property(169 self,170 &caller,171 TokenId(token_id),172 Property { key, value },173 &nesting_budget(&self.recorder),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(<CommonWeights<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 properties = properties193 .into_iter()194 .map(eth::Property::try_into)195 .collect::<Result<Vec<_>>>()?;196197 <Pallet<T>>::set_token_properties(198 self,199 &caller,200 TokenId(token_id),201 properties.into_iter(),202 &nesting_budget(&self.recorder),203 )204 .map_err(dispatch_to_evm::<T>)205 }206207 /// @notice Delete token property value.208 /// @dev Throws error if `msg.sender` has no permission to edit the property.209 /// @param tokenId ID of the token.210 /// @param key Property key.211 #[solidity(hide)]212 #[weight(<CommonWeights<T>>::delete_token_properties(1))]213 fn delete_property(&mut self, token_id: U256, caller: Caller, key: String) -> Result<()> {214 let caller = T::CrossAccountId::from_eth(caller);215 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;216 let key = <Vec<u8>>::from(key)217 .try_into()218 .map_err(|_| "key too long")?;219220 <Pallet<T>>::delete_token_property(221 self,222 &caller,223 TokenId(token_id),224 key,225 &nesting_budget(&self.recorder),226 )227 .map_err(dispatch_to_evm::<T>)228 }229230 /// @notice Delete token properties value.231 /// @dev Throws error if `msg.sender` has no permission to edit the property.232 /// @param tokenId ID of the token.233 /// @param keys Properties key.234 #[weight(<CommonWeights<T>>::delete_token_properties(keys.len() as u32))]235 fn delete_properties(236 &mut self,237 token_id: U256,238 caller: Caller,239 keys: Vec<String>,240 ) -> Result<()> {241 let caller = T::CrossAccountId::from_eth(caller);242 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;243 let keys = keys244 .into_iter()245 .map(|k| Ok(<Vec<u8>>::from(k).try_into().map_err(|_| "key too long")?))246 .collect::<Result<Vec<_>>>()?;247248 <Pallet<T>>::delete_token_properties(249 self,250 &caller,251 TokenId(token_id),252 keys.into_iter(),253 &nesting_budget(&self.recorder),254 )255 .map_err(dispatch_to_evm::<T>)256 }257258 /// @notice Get token property value.259 /// @dev Throws error if key not found260 /// @param tokenId ID of the token.261 /// @param key Property key.262 /// @return Property value bytes263 fn property(&self, token_id: U256, key: String) -> Result<Bytes> {264 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;265 let key = <Vec<u8>>::from(key)266 .try_into()267 .map_err(|_| "key too long")?;268269 let props =270 <TokenProperties<T>>::get((self.id, token_id)).ok_or("token properties not found")?;271 let prop = props.get(&key).ok_or("key not found")?;272273 Ok(prop.to_vec().into())274 }275}276277#[derive(ToLog)]278pub enum ERC721Events {279 /// @dev This emits when ownership of any NFT changes by any mechanism.280 /// This event emits when NFTs are created (`from` == 0) and destroyed281 /// (`to` == 0). Exception: during contract creation, any number of NFTs282 /// may be created and assigned without emitting Transfer. At the time of283 /// any transfer, the approved address for that NFT (if any) is reset to none.284 Transfer {285 #[indexed]286 from: Address,287 #[indexed]288 to: Address,289 #[indexed]290 token_id: U256,291 },292 /// @dev This emits when the approved address for an NFT is changed or293 /// reaffirmed. The zero address indicates there is no approved address.294 /// When a Transfer event emits, this also indicates that the approved295 /// address for that NFT (if any) is reset to none.296 Approval {297 #[indexed]298 owner: Address,299 #[indexed]300 approved: Address,301 #[indexed]302 token_id: U256,303 },304 /// @dev This emits when an operator is enabled or disabled for an owner.305 /// The operator can manage all NFTs of the owner.306 #[allow(dead_code)]307 ApprovalForAll {308 #[indexed]309 owner: Address,310 #[indexed]311 operator: Address,312 approved: bool,313 },314}315316/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension317/// @dev See https://eips.ethereum.org/EIPS/eip-721318#[solidity_interface(name = ERC721Metadata, expect_selector = 0x5b5e139f, enum(derive(PreDispatch)), enum_attr(weight))]319impl<T: Config> NonfungibleHandle<T>320where321 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,322{323 /// @notice A descriptive name for a collection of NFTs in this contract324 /// @dev real implementation of this function lies in `ERC721UniqueExtensions`325 #[solidity(hide, rename_selector = "name")]326 fn name_proxy(&self) -> String {327 self.name()328 }329330 /// @notice An abbreviated name for NFTs in this contract331 /// @dev real implementation of this function lies in `ERC721UniqueExtensions`332 #[solidity(hide, rename_selector = "symbol")]333 fn symbol_proxy(&self) -> String {334 self.symbol()335 }336337 /// @notice A distinct Uniform Resource Identifier (URI) for a given asset.338 ///339 /// @dev If the token has a `url` property and it is not empty, it is returned.340 /// 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`.341 /// If the collection property `baseURI` is empty or absent, return "" (empty string)342 /// otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix343 /// otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).344 ///345 /// @return token's const_metadata346 #[solidity(rename_selector = "tokenURI")]347 fn token_uri(&self, token_id: U256) -> Result<String> {348 let token_id_u32: u32 = token_id.try_into().map_err(|_| "token id overflow")?;349350 match get_token_property(self, token_id_u32, &key::url()).as_deref() {351 Err(_) | Ok("") => (),352 Ok(url) => {353 return Ok(url.into());354 }355 };356357 let base_uri =358 pallet_common::Pallet::<T>::get_collection_property(self.id, &key::base_uri())359 .map(BoundedVec::into_inner)360 .map(String::from_utf8)361 .transpose()362 .map_err(|e| {363 Error::Revert(alloc::format!(364 "can not convert value \"baseURI\" to string with error \"{e}\""365 ))366 })?;367368 let base_uri = match base_uri.as_deref() {369 None | Some("") => {370 return Ok("".into());371 }372 Some(base_uri) => base_uri.into(),373 };374375 Ok(376 match get_token_property(self, token_id_u32, &key::suffix()).as_deref() {377 Err(_) | Ok("") => base_uri,378 Ok(suffix) => base_uri + suffix,379 },380 )381 }382}383384/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension385/// @dev See https://eips.ethereum.org/EIPS/eip-721386#[solidity_interface(name = ERC721Enumerable, expect_selector = 0x780e9d63, enum(derive(PreDispatch)), enum_attr(weight))]387impl<T: Config> NonfungibleHandle<T> {388 /// @notice Enumerate valid NFTs389 /// @param index A counter less than `totalSupply()`390 /// @return The token identifier for the `index`th NFT,391 /// (sort order not specified)392 fn token_by_index(&self, index: U256) -> U256 {393 index394 }395396 /// @dev Not implemented397 fn token_of_owner_by_index(&self, _owner: Address, _index: U256) -> Result<U256> {398 // TODO: Not implemetable399 Err("not implemented".into())400 }401402 /// @notice Count NFTs tracked by this contract403 /// @return A count of valid NFTs tracked by this contract, where each one of404 /// them has an assigned and queryable owner not equal to the zero address405 fn total_supply(&self) -> Result<U256> {406 self.consume_store_reads(1)?;407 Ok(<Pallet<T>>::total_supply(self).into())408 }409}410411/// @title ERC-721 Non-Fungible Token Standard412/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md413#[solidity_interface(name = ERC721, events(ERC721Events), enum(derive(PreDispatch)), enum_attr(weight), expect_selector = 0x80ac58cd)]414impl<T: Config> NonfungibleHandle<T> {415 /// @notice Count all NFTs assigned to an owner416 /// @dev NFTs assigned to the zero address are considered invalid, and this417 /// function throws for queries about the zero address.418 /// @param owner An address for whom to query the balance419 /// @return The number of NFTs owned by `owner`, possibly zero420 fn balance_of(&self, owner: Address) -> Result<U256> {421 self.consume_store_reads(1)?;422 let owner = T::CrossAccountId::from_eth(owner);423 let balance = <AccountBalance<T>>::get((self.id, owner));424 Ok(balance.into())425 }426 /// @notice Find the owner of an NFT427 /// @dev NFTs assigned to zero address are considered invalid, and queries428 /// about them do throw.429 /// @param tokenId The identifier for an NFT430 /// @return The address of the owner of the NFT431 fn owner_of(&self, token_id: U256) -> Result<Address> {432 self.consume_store_reads(1)?;433 let token: TokenId = token_id.try_into()?;434 Ok(*<TokenData<T>>::get((self.id, token))435 .ok_or("token not found")?436 .owner437 .as_eth())438 }439 /// @dev Not implemented440 #[solidity(rename_selector = "safeTransferFrom")]441 fn safe_transfer_from_with_data(442 &mut self,443 _from: Address,444 _to: Address,445 _token_id: U256,446 _data: Bytes,447 ) -> Result<()> {448 // TODO: Not implemetable449 Err("not implemented".into())450 }451 /// @dev Not implemented452 fn safe_transfer_from(&mut self, _from: Address, _to: Address, _token_id: U256) -> Result<()> {453 // TODO: Not implemetable454 Err("not implemented".into())455 }456457 /// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE458 /// TO CONFIRM THAT `to` IS CAPABLE OF RECEIVING NFTS OR ELSE459 /// THEY MAY BE PERMANENTLY LOST460 /// @dev Throws unless `msg.sender` is the current owner or an authorized461 /// operator for this NFT. Throws if `from` is not the current owner. Throws462 /// if `to` is the zero address. Throws if `tokenId` is not a valid NFT.463 /// @param from The current owner of the NFT464 /// @param to The new owner465 /// @param tokenId The NFT to transfer466 #[weight(<CommonWeights<T>>::transfer_from())]467 fn transfer_from(468 &mut self,469 caller: Caller,470 from: Address,471 to: Address,472 token_id: U256,473 ) -> Result<()> {474 let caller = T::CrossAccountId::from_eth(caller);475 let from = T::CrossAccountId::from_eth(from);476 let to = T::CrossAccountId::from_eth(to);477 let token = token_id.try_into()?;478479 <Pallet<T>>::transfer_from(480 self,481 &caller,482 &from,483 &to,484 token,485 &nesting_budget(&self.recorder),486 )487 .map_err(|e| dispatch_to_evm::<T>(e.error))?;488 Ok(())489 }490491 /// @notice Set or reaffirm the approved address for an NFT492 /// @dev The zero address indicates there is no approved address.493 /// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized494 /// operator of the current owner.495 /// @param approved The new approved NFT controller496 /// @param tokenId The NFT to approve497 #[weight(<SelfWeightOf<T>>::approve())]498 fn approve(&mut self, caller: Caller, approved: Address, token_id: U256) -> Result<()> {499 let caller = T::CrossAccountId::from_eth(caller);500 let approved = T::CrossAccountId::from_eth(approved);501 let token = token_id.try_into()?;502503 <Pallet<T>>::set_allowance(self, &caller, token, Some(&approved))504 .map_err(dispatch_to_evm::<T>)?;505 Ok(())506 }507508 /// @notice Sets or unsets the approval of a given operator.509 /// The `operator` is allowed to transfer all tokens of the `caller` on their behalf.510 /// @param operator Operator511 /// @param approved Should operator status be granted or revoked?512 #[weight(<SelfWeightOf<T>>::set_allowance_for_all())]513 fn set_approval_for_all(514 &mut self,515 caller: Caller,516 operator: Address,517 approved: bool,518 ) -> Result<()> {519 let caller = T::CrossAccountId::from_eth(caller);520 let operator = T::CrossAccountId::from_eth(operator);521522 <Pallet<T>>::set_allowance_for_all(self, &caller, &operator, approved)523 .map_err(dispatch_to_evm::<T>)?;524 Ok(())525 }526527 /// @notice Get the approved address for a single NFT528 /// @dev Throws if `tokenId` is not a valid NFT529 /// @param tokenId The NFT to find the approved address for530 /// @return The approved address for this NFT, or the zero address if there is none531 fn get_approved(&self, token_id: U256) -> Result<Address> {532 let token_id = token_id.try_into()?;533 let operator = <Pallet<T>>::get_allowance(self, token_id).map_err(dispatch_to_evm::<T>)?;534 Ok(if let Some(operator) = operator {535 *operator.as_eth()536 } else {537 Address::zero()538 })539 }540541 /// @notice Tells whether the given `owner` approves the `operator`.542 #[weight(<SelfWeightOf<T>>::allowance_for_all())]543 fn is_approved_for_all(&self, owner: Address, operator: Address) -> Result<bool> {544 let owner = T::CrossAccountId::from_eth(owner);545 let operator = T::CrossAccountId::from_eth(operator);546547 Ok(<Pallet<T>>::allowance_for_all(self, &owner, &operator))548 }549}550551/// @title ERC721 Token that can be irreversibly burned (destroyed).552#[solidity_interface(name = ERC721Burnable, enum(derive(PreDispatch)), enum_attr(weight))]553impl<T: Config> NonfungibleHandle<T> {554 /// @notice Burns a specific ERC721 token.555 /// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized556 /// operator of the current owner.557 /// @param tokenId The NFT to approve558 #[weight(<SelfWeightOf<T>>::burn_item())]559 fn burn(&mut self, caller: Caller, token_id: U256) -> Result<()> {560 let caller = T::CrossAccountId::from_eth(caller);561 let token = token_id.try_into()?;562563 <Pallet<T>>::burn(self, &caller, token).map_err(dispatch_to_evm::<T>)?;564 Ok(())565 }566}567568/// @title ERC721 minting logic.569#[solidity_interface(name = ERC721UniqueMintable, enum(derive(PreDispatch)), enum_attr(weight))]570impl<T: Config> NonfungibleHandle<T> {571 /// @notice Function to mint a token.572 /// @param to The new owner573 /// @return uint256 The id of the newly minted token574 #[weight(<SelfWeightOf<T>>::create_item())]575 fn mint(&mut self, caller: Caller, to: Address) -> Result<U256> {576 let token_id: U256 = <TokensMinted<T>>::get(self.id)577 .checked_add(1)578 .ok_or("item id overflow")?579 .into();580 self.mint_check_id(caller, to, token_id)?;581 Ok(token_id)582 }583584 /// @notice Function to mint a token.585 /// @dev `tokenId` should be obtained with `nextTokenId` method,586 /// unlike standard, you can't specify it manually587 /// @param to The new owner588 /// @param tokenId ID of the minted NFT589 #[solidity(hide, rename_selector = "mint")]590 #[weight(<SelfWeightOf<T>>::create_item())]591 fn mint_check_id(&mut self, caller: Caller, to: Address, token_id: U256) -> Result<bool> {592 let caller = T::CrossAccountId::from_eth(caller);593 let to = T::CrossAccountId::from_eth(to);594 let token_id: u32 = token_id.try_into()?;595596 if <TokensMinted<T>>::get(self.id)597 .checked_add(1)598 .ok_or("item id overflow")?599 != token_id600 {601 return Err("item id should be next".into());602 }603604 <Pallet<T>>::create_item(605 self,606 &caller,607 CreateItemData::<T> {608 properties: BoundedVec::default(),609 owner: to,610 },611 &nesting_budget(&self.recorder),612 )613 .map_err(dispatch_to_evm::<T>)?;614615 Ok(true)616 }617618 /// @notice Function to mint token with the given tokenUri.619 /// @param to The new owner620 /// @param tokenUri Token URI that would be stored in the NFT properties621 /// @return uint256 The id of the newly minted token622 #[solidity(rename_selector = "mintWithTokenURI")]623 #[weight(<SelfWeightOf<T>>::create_item() + <SelfWeightOf<T>>::set_token_properties(1))]624 fn mint_with_token_uri(625 &mut self,626 caller: Caller,627 to: Address,628 token_uri: String,629 ) -> Result<U256> {630 let token_id: U256 = <TokensMinted<T>>::get(self.id)631 .checked_add(1)632 .ok_or("item id overflow")?633 .into();634 self.mint_with_token_uri_check_id(caller, to, token_id, token_uri)?;635 Ok(token_id)636 }637638 /// @notice Function to mint token with the given tokenUri.639 /// @dev `tokenId` should be obtained with `nextTokenId` method,640 /// unlike standard, you can't specify it manually641 /// @param to The new owner642 /// @param tokenId ID of the minted NFT643 /// @param tokenUri Token URI that would be stored in the NFT properties644 #[solidity(hide, rename_selector = "mintWithTokenURI")]645 #[weight(<SelfWeightOf<T>>::create_item() + <SelfWeightOf<T>>::set_token_properties(1))]646 fn mint_with_token_uri_check_id(647 &mut self,648 caller: Caller,649 to: Address,650 token_id: U256,651 token_uri: String,652 ) -> Result<bool> {653 let key = key::url();654 let permission = get_token_permission::<T>(self.id, &key)?;655 if !permission.collection_admin {656 return Err("operation is not allowed".into());657 }658659 let caller = T::CrossAccountId::from_eth(caller);660 let to = T::CrossAccountId::from_eth(to);661 let token_id: u32 = token_id.try_into().map_err(|_| "amount overflow")?;662663 if <TokensMinted<T>>::get(self.id)664 .checked_add(1)665 .ok_or("item id overflow")?666 != token_id667 {668 return Err("item id should be next".into());669 }670671 let mut properties = CollectionPropertiesVec::default();672 properties673 .try_push(Property {674 key,675 value: token_uri676 .into_bytes()677 .try_into()678 .map_err(|_| "token uri is too long")?,679 })680 .map_err(|e| Error::Revert(alloc::format!("can't add property: {e:?}")))?;681682 <Pallet<T>>::create_item(683 self,684 &caller,685 CreateItemData::<T> {686 properties,687 owner: to,688 },689 &nesting_budget(&self.recorder),690 )691 .map_err(dispatch_to_evm::<T>)?;692 Ok(true)693 }694}695696fn get_token_property<T: Config>(697 collection: &CollectionHandle<T>,698 token_id: u32,699 key: &up_data_structs::PropertyKey,700) -> Result<String> {701 collection.consume_store_reads(1)?;702 let properties = <TokenProperties<T>>::try_get((collection.id, token_id))703 .map_err(|_| Error::Revert("token properties not found".into()))?;704 if let Some(property) = properties.get(key) {705 return Ok(String::from_utf8_lossy(property).into());706 }707708 Err("property tokenURI not found".into())709}710711fn get_token_permission<T: Config>(712 collection_id: CollectionId,713 key: &PropertyKey,714) -> Result<PropertyPermission> {715 let token_property_permissions = CollectionPropertyPermissions::<T>::try_get(collection_id)716 .map_err(|_| Error::Revert("no permissions for collection".into()))?;717 let a = token_property_permissions718 .get(key)719 .map(Clone::clone)720 .ok_or_else(|| {721 let key = String::from_utf8(key.clone().into_inner()).unwrap_or_default();722 Error::Revert(alloc::format!("no permission for key {key}"))723 })?;724 Ok(a)725}726727/// @title Unique extensions for ERC721.728#[solidity_interface(name = ERC721UniqueExtensions, enum(derive(PreDispatch)), enum_attr(weight))]729impl<T: Config> NonfungibleHandle<T>730where731 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,732{733 /// @notice A descriptive name for a collection of NFTs in this contract734 fn name(&self) -> String {735 decode_utf16(self.name.iter().copied())736 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))737 .collect::<String>()738 }739740 /// @notice An abbreviated name for NFTs in this contract741 fn symbol(&self) -> String {742 String::from_utf8_lossy(&self.token_prefix).into()743 }744745 /// @notice A description for the collection.746 fn description(&self) -> String {747 decode_utf16(self.description.iter().copied())748 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))749 .collect::<String>()750 }751752 /// Returns the owner (in cross format) of the token.753 ///754 /// @param tokenId Id for the token.755 #[solidity(hide)]756 fn cross_owner_of(&self, token_id: U256) -> Result<eth::CrossAddress> {757 Self::owner_of_cross(self, token_id)758 }759760 /// Returns the owner (in cross format) of the token.761 ///762 /// @param tokenId Id for the token.763 fn owner_of_cross(&self, token_id: U256) -> Result<eth::CrossAddress> {764 Self::token_owner(self, token_id.try_into()?)765 .map(|o| eth::CrossAddress::from_sub_cross_account::<T>(&o))766 .map_err(|_| Error::Revert("token not found".into()))767 }768769 /// @notice Count all NFTs assigned to an owner770 /// @param owner An cross address for whom to query the balance771 /// @return The number of NFTs owned by `owner`, possibly zero772 fn balance_of_cross(&self, owner: eth::CrossAddress) -> Result<U256> {773 self.consume_store_reads(1)?;774 let balance = <AccountBalance<T>>::get((self.id, owner.into_sub_cross_account::<T>()?));775 Ok(balance.into())776 }777778 /// Returns the token properties.779 ///780 /// @param tokenId Id for the token.781 /// @param keys Properties keys. Empty keys for all propertyes.782 /// @return Vector of properties key/value pairs.783 fn properties(&self, token_id: U256, keys: Vec<String>) -> Result<Vec<eth::Property>> {784 let keys = keys785 .into_iter()786 .map(|key| {787 <Vec<u8>>::from(key)788 .try_into()789 .map_err(|_| Error::Revert("key too large".into()))790 })791 .collect::<Result<Vec<_>>>()?;792793 <Self as CommonCollectionOperations<T>>::token_properties(794 self,795 token_id.try_into()?,796 if keys.is_empty() { None } else { Some(keys) },797 )798 .into_iter()799 .map(eth::Property::try_from)800 .collect::<Result<Vec<_>>>()801 }802803 /// @notice Set or reaffirm the approved address for an NFT804 /// @dev The zero address indicates there is no approved address.805 /// @dev Throws unless `msg.sender` is the current NFT owner, or an authorized806 /// operator of the current owner.807 /// @param approved The new substrate address approved NFT controller808 /// @param tokenId The NFT to approve809 #[weight(<SelfWeightOf<T>>::approve())]810 fn approve_cross(811 &mut self,812 caller: Caller,813 approved: eth::CrossAddress,814 token_id: U256,815 ) -> Result<()> {816 let caller = T::CrossAccountId::from_eth(caller);817 let approved = approved.into_sub_cross_account::<T>()?;818 let token = token_id.try_into()?;819820 <Pallet<T>>::set_allowance(self, &caller, token, Some(&approved))821 .map_err(dispatch_to_evm::<T>)?;822 Ok(())823 }824825 /// @notice Transfer ownership of an NFT826 /// @dev Throws unless `msg.sender` is the current owner. Throws if `to`827 /// is the zero address. Throws if `tokenId` is not a valid NFT.828 /// @param to The new owner829 /// @param tokenId The NFT to transfer830 #[weight(<CommonWeights<T>>::transfer())]831 fn transfer(&mut self, caller: Caller, to: Address, token_id: U256) -> Result<()> {832 let caller = T::CrossAccountId::from_eth(caller);833 let to = T::CrossAccountId::from_eth(to);834 let token = token_id.try_into()?;835836 <Pallet<T>>::transfer(self, &caller, &to, token, &nesting_budget(&self.recorder))837 .map_err(|e| dispatch_to_evm::<T>(e.error))?;838 Ok(())839 }840841 /// @notice Transfer ownership of an NFT842 /// @dev Throws unless `msg.sender` is the current owner. Throws if `to`843 /// is the zero address. Throws if `tokenId` is not a valid NFT.844 /// @param to The new owner845 /// @param tokenId The NFT to transfer846 #[weight(<CommonWeights<T>>::transfer())]847 fn transfer_cross(848 &mut self,849 caller: Caller,850 to: eth::CrossAddress,851 token_id: U256,852 ) -> Result<()> {853 let caller = T::CrossAccountId::from_eth(caller);854 let to = to.into_sub_cross_account::<T>()?;855 let token = token_id.try_into()?;856857 <Pallet<T>>::transfer(self, &caller, &to, token, &nesting_budget(&self.recorder))858 .map_err(|e| dispatch_to_evm::<T>(e.error))?;859 Ok(())860 }861862 /// @notice Transfer ownership of an NFT from cross account address to cross account address863 /// @dev Throws unless `msg.sender` is the current owner. Throws if `to`864 /// is the zero address. Throws if `tokenId` is not a valid NFT.865 /// @param from Cross acccount address of current owner866 /// @param to Cross acccount address of new owner867 /// @param tokenId The NFT to transfer868 #[weight(<CommonWeights<T>>::transfer_from())]869 fn transfer_from_cross(870 &mut self,871 caller: Caller,872 from: eth::CrossAddress,873 to: eth::CrossAddress,874 token_id: U256,875 ) -> Result<()> {876 let caller = T::CrossAccountId::from_eth(caller);877 let from = from.into_sub_cross_account::<T>()?;878 let to = to.into_sub_cross_account::<T>()?;879 let token_id = token_id.try_into()?;880881 Pallet::<T>::transfer_from(882 self,883 &caller,884 &from,885 &to,886 token_id,887 &nesting_budget(&self.recorder),888 )889 .map_err(|e| dispatch_to_evm::<T>(e.error))?;890 Ok(())891 }892893 /// @notice Burns a specific ERC721 token.894 /// @dev Throws unless `msg.sender` is the current owner or an authorized895 /// operator for this NFT. Throws if `from` is not the current owner. Throws896 /// if `to` is the zero address. Throws if `tokenId` is not a valid NFT.897 /// @param from The current owner of the NFT898 /// @param tokenId The NFT to transfer899 #[solidity(hide)]900 #[weight(<SelfWeightOf<T>>::burn_from())]901 fn burn_from(&mut self, caller: Caller, from: Address, token_id: U256) -> Result<()> {902 let caller = T::CrossAccountId::from_eth(caller);903 let from = T::CrossAccountId::from_eth(from);904 let token = token_id.try_into()?;905906 <Pallet<T>>::burn_from(self, &caller, &from, token, &nesting_budget(&self.recorder))907 .map_err(dispatch_to_evm::<T>)?;908 Ok(())909 }910911 /// @notice Burns a specific ERC721 token.912 /// @dev Throws unless `msg.sender` is the current owner or an authorized913 /// operator for this NFT. Throws if `from` is not the current owner. Throws914 /// if `to` is the zero address. Throws if `tokenId` is not a valid NFT.915 /// @param from The current owner of the NFT916 /// @param tokenId The NFT to transfer917 #[weight(<SelfWeightOf<T>>::burn_from())]918 fn burn_from_cross(919 &mut self,920 caller: Caller,921 from: eth::CrossAddress,922 token_id: U256,923 ) -> Result<()> {924 let caller = T::CrossAccountId::from_eth(caller);925 let from = from.into_sub_cross_account::<T>()?;926 let token = token_id.try_into()?;927928 <Pallet<T>>::burn_from(self, &caller, &from, token, &nesting_budget(&self.recorder))929 .map_err(dispatch_to_evm::<T>)?;930 Ok(())931 }932933 /// @notice Returns next free NFT ID.934 fn next_token_id(&self) -> Result<U256> {935 self.consume_store_reads(1)?;936 Ok(<Pallet<T>>::next_token_id(self)937 .map_err(dispatch_to_evm::<T>)?938 .into())939 }940941 /// @notice Function to mint multiple tokens.942 /// @dev `tokenIds` should be an array of consecutive numbers and first number943 /// should be obtained with `nextTokenId` method944 /// @param to The new owner945 /// @param tokenIds IDs of the minted NFTs946 #[solidity(hide)]947 #[weight(<SelfWeightOf<T>>::create_multiple_items(token_ids.len() as u32))]948 fn mint_bulk(&mut self, caller: Caller, to: Address, token_ids: Vec<U256>) -> Result<bool> {949 let caller = T::CrossAccountId::from_eth(caller);950 let to = T::CrossAccountId::from_eth(to);951 let mut expected_index = <TokensMinted<T>>::get(self.id)952 .checked_add(1)953 .ok_or("item id overflow")?;954955 let total_tokens = token_ids.len();956 for id in token_ids.into_iter() {957 let id: u32 = id.try_into().map_err(|_| "token id overflow")?;958 if id != expected_index {959 return Err("item id should be next".into());960 }961 expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;962 }963 let data = (0..total_tokens)964 .map(|_| CreateItemData::<T> {965 properties: BoundedVec::default(),966 owner: to.clone(),967 })968 .collect();969970 <Pallet<T>>::create_multiple_items(self, &caller, data, &nesting_budget(&self.recorder))971 .map_err(dispatch_to_evm::<T>)?;972 Ok(true)973 }974975 /// @notice Function to mint a token.976 /// @param data Array of pairs of token owner and token's properties for minted token977 #[weight(<SelfWeightOf<T>>::create_multiple_items(data.len() as u32) + <SelfWeightOf<T>>::set_token_properties(data.len() as u32))]978 fn mint_bulk_cross(&mut self, caller: Caller, data: Vec<MintTokenData>) -> Result<bool> {979 let caller = T::CrossAccountId::from_eth(caller);980981 let mut create_nft_data = Vec::with_capacity(data.len());982 for MintTokenData { owner, properties } in data {983 let owner = owner.into_sub_cross_account::<T>()?;984 create_nft_data.push(CreateItemData::<T> {985 properties: properties986 .into_iter()987 .map(|property| property.try_into())988 .collect::<Result<Vec<_>>>()?989 .try_into()990 .map_err(|_| "too many properties")?,991 owner,992 });993 }994995 <Pallet<T>>::create_multiple_items(996 self,997 &caller,998 create_nft_data,999 &nesting_budget(&self.recorder),1000 )1001 .map_err(dispatch_to_evm::<T>)?;1002 Ok(true)1003 }10041005 /// @notice Function to mint multiple tokens with the given tokenUris.1006 /// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive1007 /// numbers and first number should be obtained with `nextTokenId` method1008 /// @param to The new owner1009 /// @param tokens array of pairs of token ID and token URI for minted tokens1010 #[solidity(hide, rename_selector = "mintBulkWithTokenURI")]1011 #[weight(<SelfWeightOf<T>>::create_multiple_items(tokens.len() as u32) + <SelfWeightOf<T>>::set_token_properties(tokens.len() as u32))]1012 fn mint_bulk_with_token_uri(1013 &mut self,1014 caller: Caller,1015 to: Address,1016 tokens: Vec<TokenUri>,1017 ) -> Result<bool> {1018 let key = key::url();1019 let caller = T::CrossAccountId::from_eth(caller);1020 let to = T::CrossAccountId::from_eth(to);1021 let mut expected_index = <TokensMinted<T>>::get(self.id)1022 .checked_add(1)1023 .ok_or("item id overflow")?;10241025 let mut data = Vec::with_capacity(tokens.len());1026 for TokenUri { id, uri } in tokens {1027 let id: u32 = id.try_into().map_err(|_| "token id overflow")?;1028 if id != expected_index {1029 return Err("item id should be next".into());1030 }1031 expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;10321033 let mut properties = CollectionPropertiesVec::default();1034 properties1035 .try_push(Property {1036 key: key.clone(),1037 value: uri1038 .into_bytes()1039 .try_into()1040 .map_err(|_| "token uri is too long")?,1041 })1042 .map_err(|e| Error::Revert(alloc::format!("can't add property: {e:?}")))?;10431044 data.push(CreateItemData::<T> {1045 properties,1046 owner: to.clone(),1047 });1048 }10491050 <Pallet<T>>::create_multiple_items(self, &caller, data, &nesting_budget(&self.recorder))1051 .map_err(dispatch_to_evm::<T>)?;1052 Ok(true)1053 }10541055 /// @notice Function to mint a token.1056 /// @param to The new owner crossAccountId1057 /// @param properties Properties of minted token1058 /// @return uint256 The id of the newly minted token1059 #[weight(<SelfWeightOf<T>>::create_item() + <SelfWeightOf<T>>::set_token_properties(properties.len() as u32))]1060 fn mint_cross(1061 &mut self,1062 caller: Caller,1063 to: eth::CrossAddress,1064 properties: Vec<eth::Property>,1065 ) -> Result<U256> {1066 let token_id = <TokensMinted<T>>::get(self.id)1067 .checked_add(1)1068 .ok_or("item id overflow")?;10691070 let to = to.into_sub_cross_account::<T>()?;10711072 let properties = properties1073 .into_iter()1074 .map(eth::Property::try_into)1075 .collect::<Result<Vec<_>>>()?1076 .try_into()1077 .map_err(|_| Error::Revert("too many properties".to_string()))?;10781079 let caller = T::CrossAccountId::from_eth(caller);10801081 <Pallet<T>>::create_item(1082 self,1083 &caller,1084 CreateItemData::<T> {1085 properties,1086 owner: to,1087 },1088 &nesting_budget(&self.recorder),1089 )1090 .map_err(dispatch_to_evm::<T>)?;10911092 Ok(token_id.into())1093 }10941095 /// @notice Returns collection helper contract address1096 fn collection_helper_address(&self) -> Address {1097 T::ContractAddress::get()1098 }1099}11001101#[solidity_interface(1102 name = UniqueNFT,1103 is(1104 ERC721,1105 ERC721Enumerable,1106 ERC721UniqueExtensions,1107 ERC721UniqueMintable,1108 ERC721Burnable,1109 ERC721Metadata(if(this.flags.erc721metadata)),1110 Collection(via(common_mut returns CollectionHandle<T>)),1111 TokenProperties,1112 ),1113 enum(derive(PreDispatch)),1114)]1115impl<T: Config> NonfungibleHandle<T> where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]> {}11161117// Not a tests, but code generators1118generate_stubgen!(gen_impl, UniqueNFTCall<()>, true);1119generate_stubgen!(gen_iface, UniqueNFTCall<()>, false);11201121impl<T: Config> CommonEvmHandler for NonfungibleHandle<T>1122where1123 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,1124{1125 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueNFT.raw");11261127 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {1128 call::<T, UniqueNFTCall<T>, _, _>(handle, self)1129 }1130}pallets/refungible/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/refungible/src/benchmarking.rs
+++ b/pallets/refungible/src/benchmarking.rs
@@ -421,114 +421,30 @@
Ok(())
}
- // set_token_properties {
- // let b in 0..MAX_PROPERTIES_PER_ITEM;
- // bench_init!{
- // owner: sub; collection: collection(owner);
- // owner: cross_from_sub;
- // };
- // let perms = (0..b).map(|k| PropertyKeyPermission {
- // key: property_key(k as usize),
- // permission: PropertyPermission {
- // mutable: false,
- // collection_admin: true,
- // token_owner: true,
- // },
- // }).collect::<Vec<_>>();
- // <Pallet<T>>::set_token_property_permissions(&collection, &owner, perms)?;
- // let props = (0..b).map(|k| Property {
- // key: property_key(k as usize),
- // value: property_value(),
- // }).collect::<Vec<_>>();
- // let item = create_max_item(&collection, &owner, [(owner.clone(), 200)])?;
- // }: {<Pallet<T>>::set_token_properties(&collection, &owner, item, props.into_iter(), &Unlimited)?}
-
- // load_token_properties {
- // bench_init!{
- // owner: sub; collection: collection(owner);
- // owner: cross_from_sub;
- // };
-
- // let item = create_max_item(&collection, &owner, [(owner.clone(), 200)])?;
- // }: {
- // pallet_common::BenchmarkPropertyWriter::<T>::load_token_properties(
- // &collection,
- // item,
- // )
- // }
-
- // write_token_properties {
- // let b in 0..MAX_PROPERTIES_PER_ITEM;
- // bench_init!{
- // owner: sub; collection: collection(owner);
- // owner: cross_from_sub;
- // };
-
- // let perms = (0..b).map(|k| PropertyKeyPermission {
- // key: property_key(k as usize),
- // permission: PropertyPermission {
- // mutable: false,
- // collection_admin: true,
- // token_owner: true,
- // },
- // }).collect::<Vec<_>>();
- // <Pallet<T>>::set_token_property_permissions(&collection, &owner, perms)?;
- // let props = (0..b).map(|k| Property {
- // key: property_key(k as usize),
- // value: property_value(),
- // }).collect::<Vec<_>>();
- // let item = create_max_item(&collection, &owner, [(owner.clone(), 200)])?;
-
- // let lazy_collection_info = pallet_common::BenchmarkPropertyWriter::<T>::load_collection_info(
- // &collection,
- // &owner,
- // );
- // }: {
- // let mut property_writer = pallet_common::BenchmarkPropertyWriter::new(&collection, lazy_collection_info);
-
- // property_writer.write_token_properties(
- // item,
- // props.into_iter(),
- // crate::erc::ERC721TokenEvent::TokenChanged {
- // token_id: item.into(),
- // }
- // .to_log(T::ContractAddress::get()),
- // )?
- // }
-
#[benchmark]
- fn set_token_property_permissions(
- b: Linear<0, MAX_PROPERTIES_PER_ITEM>,
- ) -> Result<(), BenchmarkError> {
+ fn load_token_properties() -> Result<(), BenchmarkError> {
bench_init! {
owner: sub; collection: collection(owner);
owner: cross_from_sub;
};
- let perms = (0..b)
- .map(|k| PropertyKeyPermission {
- key: property_key(k as usize),
- permission: PropertyPermission {
- mutable: false,
- collection_admin: false,
- token_owner: false,
- },
- })
- .collect::<Vec<_>>();
+ let item = create_max_item(&collection, &owner, [(owner.clone(), 200)])?;
+
#[block]
{
- <Pallet<T>>::set_token_property_permissions(&collection, &owner, perms)?;
+ pallet_common::BenchmarkPropertyWriter::<T>::load_token_properties(&collection, item);
}
Ok(())
}
#[benchmark]
- fn set_token_properties(b: Linear<0, MAX_PROPERTIES_PER_ITEM>) -> Result<(), BenchmarkError> {
+ fn write_token_properties(b: Linear<0, MAX_PROPERTIES_PER_ITEM>) -> Result<(), BenchmarkError> {
bench_init! {
owner: sub; collection: collection(owner);
owner: cross_from_sub;
};
+
let perms = (0..b)
.map(|k| PropertyKeyPermission {
key: property_key(k as usize),
@@ -548,73 +464,29 @@
.collect::<Vec<_>>();
let item = create_max_item(&collection, &owner, [(owner.clone(), 200)])?;
+ let lazy_collection_info =
+ pallet_common::BenchmarkPropertyWriter::<T>::load_collection_info(&collection, &owner);
+
#[block]
{
- <Pallet<T>>::set_token_properties(
- &collection,
- &owner,
+ let mut property_writer =
+ pallet_common::BenchmarkPropertyWriter::new(&collection, lazy_collection_info);
+
+ property_writer.write_token_properties(
item,
props.into_iter(),
- &Unlimited,
+ crate::erc::ERC721TokenEvent::TokenChanged {
+ token_id: item.into(),
+ }
+ .to_log(T::ContractAddress::get()),
)?;
}
Ok(())
}
- // TODO:
#[benchmark]
- fn init_token_properties(b: Linear<0, MAX_PROPERTIES_PER_ITEM>) -> Result<(), BenchmarkError> {
- // bench_init! {
- // owner: sub; collection: collection(owner);
- // owner: cross_from_sub;
- // };
-
- // let perms = (0..b)
- // .map(|k| PropertyKeyPermission {
- // key: property_key(k as usize),
- // permission: PropertyPermission {
- // mutable: false,
- // collection_admin: true,
- // token_owner: true,
- // },
- // })
- // .collect::<Vec<_>>();
- // <Pallet<T>>::set_token_property_permissions(&collection, &owner, perms)?;
-
- #[block]
- {}
- // let props = (0..b).map(|k| Property {
- // key: property_key(k as usize),
- // value: property_value(),
- // }).collect::<Vec<_>>();
- // let item = create_max_item(&collection, &owner, [(owner.clone(), 200)])?;
-
- // let (is_collection_admin, property_permissions) = load_is_admin_and_property_permissions(&collection, &owner)
- // let mut property_writer = pallet_common::collection_info_loaded_property_writer(
- // &collection,
- // is_collection_admin,
- // property_permissions,
- // );
-
- // #[block]
- // {
- // property_writer.write_token_properties(
- // true,
- // item,
- // props.into_iter(),
- // crate::erc::ERC721TokenEvent::TokenChanged {
- // token_id: item.into(),
- // }
- // .to_log(T::ContractAddress::get()),
- // )?;
- // }
-
- Ok(())
- }
-
- #[benchmark]
- fn delete_token_properties(
+ fn set_token_property_permissions(
b: Linear<0, MAX_PROPERTIES_PER_ITEM>,
) -> Result<(), BenchmarkError> {
bench_init! {
@@ -625,38 +497,16 @@
.map(|k| PropertyKeyPermission {
key: property_key(k as usize),
permission: PropertyPermission {
- mutable: true,
- collection_admin: true,
- token_owner: true,
+ mutable: false,
+ collection_admin: false,
+ token_owner: false,
},
- })
- .collect::<Vec<_>>();
- <Pallet<T>>::set_token_property_permissions(&collection, &owner, perms)?;
- let props = (0..b)
- .map(|k| Property {
- key: property_key(k as usize),
- value: property_value(),
})
.collect::<Vec<_>>();
- let item = create_max_item(&collection, &owner, [(owner.clone(), 200)])?;
- <Pallet<T>>::set_token_properties(
- &collection,
- &owner,
- item,
- props.into_iter(),
- &Unlimited,
- )?;
- let to_delete = (0..b).map(|k| property_key(k as usize)).collect::<Vec<_>>();
#[block]
{
- <Pallet<T>>::delete_token_properties(
- &collection,
- &owner,
- item,
- to_delete.into_iter(),
- &Unlimited,
- )?;
+ <Pallet<T>>::set_token_property_permissions(&collection, &owner, perms)?;
}
Ok(())
@@ -673,22 +523,6 @@
#[block]
{
<Pallet<T>>::repartition(&collection, &owner, item, 200)?;
- }
-
- Ok(())
- }
-
- #[benchmark]
- fn token_owner() -> Result<(), BenchmarkError> {
- bench_init! {
- owner: sub; collection: collection(owner);
- sender: cross_from_sub(owner); owner: cross_sub;
- };
- let item = create_max_item(&collection, &sender, [(owner, 100)])?;
-
- #[block]
- {
- <Pallet<T>>::token_owner(collection.id, item).unwrap();
}
Ok(())
pallets/refungible/src/common.rsdiffbeforeafterboth--- a/pallets/refungible/src/common.rs
+++ b/pallets/refungible/src/common.rs
@@ -47,35 +47,27 @@
pub struct CommonWeights<T: Config>(PhantomData<T>);
impl<T: Config> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T> {
fn create_multiple_items(data: &[up_data_structs::CreateItemData]) -> Weight {
- <SelfWeightOf<T>>::create_multiple_items(data.len() as u32).saturating_add(
- write_token_properties_total_weight::<T, _>(
- data.iter().map(|data| match data {
- up_data_structs::CreateItemData::ReFungible(rft_data) => {
- rft_data.properties.len() as u32
- }
- _ => 0,
- }),
- <SelfWeightOf<T>>::write_token_properties,
- ),
+ mint_with_props_weight::<T>(
+ <SelfWeightOf<T>>::create_multiple_items(data.len() as u32),
+ data.iter().map(|data| match data {
+ up_data_structs::CreateItemData::ReFungible(rft_data) => {
+ rft_data.properties.len() as u32
+ }
+ _ => 0,
+ }),
)
}
fn create_multiple_items_ex(call: &CreateItemExData<T::CrossAccountId>) -> Weight {
match call {
- CreateItemExData::RefungibleMultipleOwners(i) => {
- <SelfWeightOf<T>>::create_multiple_items_ex_multiple_owners(i.users.len() as u32)
- .saturating_add(write_token_properties_total_weight::<T, _>(
- [i.properties.len() as u32].into_iter(),
- <SelfWeightOf<T>>::write_token_properties,
- ))
- }
- CreateItemExData::RefungibleMultipleItems(i) => {
- <SelfWeightOf<T>>::create_multiple_items_ex_multiple_items(i.len() as u32)
- .saturating_add(write_token_properties_total_weight::<T, _>(
- i.iter().map(|d| d.properties.len() as u32),
- <SelfWeightOf<T>>::write_token_properties,
- ))
- }
+ CreateItemExData::RefungibleMultipleOwners(i) => mint_with_props_weight::<T>(
+ <SelfWeightOf<T>>::create_multiple_items_ex_multiple_owners(i.users.len() as u32),
+ [i.properties.len() as u32].into_iter(),
+ ),
+ CreateItemExData::RefungibleMultipleItems(i) => mint_with_props_weight::<T>(
+ <SelfWeightOf<T>>::create_multiple_items_ex_multiple_items(i.len() as u32),
+ i.iter().map(|d| d.properties.len() as u32),
+ ),
_ => Weight::zero(),
}
}
@@ -138,6 +130,16 @@
}
}
+pub(crate) fn mint_with_props_weight<T: Config>(
+ create_no_data_weight: Weight,
+ tokens: impl Iterator<Item = u32> + Clone,
+) -> Weight {
+ create_no_data_weight.saturating_add(write_token_properties_total_weight::<T, _>(
+ tokens,
+ <SelfWeightOf<T>>::write_token_properties,
+ ))
+}
+
fn map_create_data<T: Config>(
data: up_data_structs::CreateItemData,
to: &T::CrossAccountId,
pallets/refungible/src/erc.rsdiffbeforeafterboth--- a/pallets/refungible/src/erc.rs
+++ b/pallets/refungible/src/erc.rs
@@ -50,8 +50,10 @@
};
use crate::{
- common::CommonWeights, weights::WeightInfo, AccountBalance, Balance, Config, CreateItemData,
- Pallet, RefungibleHandle, SelfWeightOf, TokenProperties, TokensMinted, TotalSupply,
+ common::{mint_with_props_weight, CommonWeights},
+ weights::WeightInfo,
+ AccountBalance, Balance, Config, CreateItemData, Pallet, RefungibleHandle, SelfWeightOf,
+ TokenProperties, TokensMinted, TotalSupply,
};
frontier_contract! {
@@ -661,7 +663,7 @@
/// @param tokenUri Token URI that would be stored in the NFT properties
/// @return uint256 The id of the newly minted token
#[solidity(rename_selector = "mintWithTokenURI")]
- #[weight(<SelfWeightOf<T>>::create_item() + <SelfWeightOf<T>>::set_token_properties(1))]
+ #[weight(mint_with_props_weight::<T>(<SelfWeightOf<T>>::create_item(), [1].into_iter()))]
fn mint_with_token_uri(
&mut self,
caller: Caller,
@@ -683,7 +685,7 @@
/// @param tokenId ID of the minted RFT
/// @param tokenUri Token URI that would be stored in the RFT properties
#[solidity(hide, rename_selector = "mintWithTokenURI")]
- #[weight(<SelfWeightOf<T>>::create_item() + <SelfWeightOf<T>>::set_token_properties(1))]
+ #[weight(mint_with_props_weight::<T>(<SelfWeightOf<T>>::create_item(), [1].into_iter()))]
fn mint_with_token_uri_check_id(
&mut self,
caller: Caller,
@@ -1052,22 +1054,26 @@
}
/// @notice Function to mint a token.
- /// @param tokenProperties Properties of minted token
- #[weight(if token_properties.len() == 1 {
- <SelfWeightOf<T>>::create_multiple_items_ex_multiple_owners(token_properties.iter().next().unwrap().owners.len() as u32)
+ /// @param tokensData Data of minted token(s)
+ #[weight(if tokens_data.len() == 1 {
+ let token_data = tokens_data.first().unwrap();
+
+ mint_with_props_weight::<T>(
+ <SelfWeightOf<T>>::create_multiple_items_ex_multiple_owners(token_data.owners.len() as u32),
+ [token_data.properties.len() as u32].into_iter(),
+ )
} else {
- <SelfWeightOf<T>>::create_multiple_items_ex_multiple_items(token_properties.len() as u32)
- } + <SelfWeightOf<T>>::set_token_properties(token_properties.len() as u32))]
- fn mint_bulk_cross(
- &mut self,
- caller: Caller,
- token_properties: Vec<MintTokenData>,
- ) -> Result<bool> {
+ mint_with_props_weight::<T>(
+ <SelfWeightOf<T>>::create_multiple_items_ex_multiple_items(tokens_data.len() as u32),
+ tokens_data.iter().map(|d| d.properties.len() as u32),
+ )
+ })]
+ fn mint_bulk_cross(&mut self, caller: Caller, tokens_data: Vec<MintTokenData>) -> Result<bool> {
let caller = T::CrossAccountId::from_eth(caller);
- let has_multiple_tokens = token_properties.len() > 1;
+ let has_multiple_tokens = tokens_data.len() > 1;
- let mut create_rft_data = Vec::with_capacity(token_properties.len());
- for MintTokenData { owners, properties } in token_properties {
+ let mut create_rft_data = Vec::with_capacity(tokens_data.len());
+ for MintTokenData { owners, properties } in tokens_data {
let has_multiple_owners = owners.len() > 1;
if has_multiple_tokens & has_multiple_owners {
return Err(
@@ -1108,7 +1114,12 @@
/// @param to The new owner
/// @param tokens array of pairs of token ID and token URI for minted tokens
#[solidity(hide, rename_selector = "mintBulkWithTokenURI")]
- #[weight(<SelfWeightOf<T>>::create_multiple_items(tokens.len() as u32) + <SelfWeightOf<T>>::set_token_properties(tokens.len() as u32))]
+ #[weight(
+ mint_with_props_weight::<T>(
+ <SelfWeightOf<T>>::create_multiple_items(tokens.len() as u32),
+ tokens.iter().map(|_| 1),
+ )
+ )]
fn mint_bulk_with_token_uri(
&mut self,
caller: Caller,
@@ -1162,7 +1173,7 @@
/// @param to The new owner crossAccountId
/// @param properties Properties of minted token
/// @return uint256 The id of the newly minted token
- #[weight(<SelfWeightOf<T>>::create_item() + <SelfWeightOf<T>>::set_token_properties(properties.len() as u32))]
+ #[weight(mint_with_props_weight::<T>(<SelfWeightOf<T>>::create_item(), [properties.len() as u32].into_iter()))]
fn mint_cross(
&mut self,
caller: Caller,