difftreelog
fix forward collection flags
in: master
10 files changed
pallets/common/src/dispatch.rsdiffbeforeafterboth--- a/pallets/common/src/dispatch.rs
+++ b/pallets/common/src/dispatch.rs
@@ -9,7 +9,7 @@
traits::Get,
};
use sp_runtime::DispatchError;
-use up_data_structs::{CollectionId, CreateCollectionData};
+use up_data_structs::{CollectionId, CreateCollectionData, CollectionFlags};
use crate::{pallet::Config, CommonCollectionOperations, CollectionHandle};
@@ -80,6 +80,7 @@
sender: T::CrossAccountId,
payer: T::CrossAccountId,
data: CreateCollectionData<T::AccountId>,
+ flags: CollectionFlags,
) -> Result<CollectionId, DispatchError>;
/// Delete the collection.
pallets/fungible/src/lib.rsdiffbeforeafterboth--- a/pallets/fungible/src/lib.rs
+++ b/pallets/fungible/src/lib.rs
@@ -212,8 +212,9 @@
owner: T::CrossAccountId,
payer: T::CrossAccountId,
data: CreateCollectionData<T::AccountId>,
+ flags: CollectionFlags,
) -> Result<CollectionId, DispatchError> {
- <PalletCommon<T>>::init_collection(owner, payer, data, CollectionFlags::default())
+ <PalletCommon<T>>::init_collection(owner, payer, data, flags)
}
/// Initializes the collection with ForeignCollection flag. Returns [CollectionId] on success, [DispatchError] otherwise.
pallets/nonfungible/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/benchmarking.rs
+++ b/pallets/nonfungible/src/benchmarking.rs
@@ -55,7 +55,7 @@
owner,
CollectionMode::NFT,
|owner: T::CrossAccountId, data| {
- <Pallet<T>>::init_collection(owner.clone(), owner, data, true)
+ <Pallet<T>>::init_collection(owner.clone(), owner, data, Default::default())
},
NonfungibleHandle::cast,
)
pallets/nonfungible/src/lib.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -408,17 +408,9 @@
owner: T::CrossAccountId,
payer: T::CrossAccountId,
data: CreateCollectionData<T::AccountId>,
- is_external: bool,
+ flags: CollectionFlags,
) -> Result<CollectionId, DispatchError> {
- <PalletCommon<T>>::init_collection(
- owner,
- payer,
- data,
- CollectionFlags {
- external: is_external,
- ..Default::default()
- },
- )
+ <PalletCommon<T>>::init_collection(owner, payer, data, flags)
}
/// Destroy NFT collection
pallets/proxy-rmrk-core/src/lib.rsdiffbeforeafterboth--- a/pallets/proxy-rmrk-core/src/lib.rs
+++ b/pallets/proxy-rmrk-core/src/lib.rs
@@ -1448,7 +1448,15 @@
data: CreateCollectionData<T::AccountId>,
properties: impl Iterator<Item = Property>,
) -> Result<CollectionId, DispatchError> {
- let collection_id = <PalletNft<T>>::init_collection(sender.clone(), sender, data, true);
+ let collection_id = <PalletNft<T>>::init_collection(
+ sender.clone(),
+ sender,
+ data,
+ up_data_structs::CollectionFlags {
+ external: true,
+ ..Default::default()
+ },
+ );
if let Err(DispatchError::Arithmetic(_)) = &collection_id {
return Err(<Error<T>>::NoAvailableCollectionId.into());
pallets/proxy-rmrk-equip/src/lib.rsdiffbeforeafterboth--- a/pallets/proxy-rmrk-equip/src/lib.rs
+++ b/pallets/proxy-rmrk-equip/src/lib.rs
@@ -254,7 +254,10 @@
cross_sender.clone(),
cross_sender.clone(),
data,
- true,
+ up_data_structs::CollectionFlags {
+ external: true,
+ ..Default::default()
+ },
);
if let Err(DispatchError::Arithmetic(_)) = &collection_id_res {
pallets/refungible/src/lib.rsdiffbeforeafterboth--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -371,8 +371,9 @@
owner: T::CrossAccountId,
payer: T::CrossAccountId,
data: CreateCollectionData<T::AccountId>,
+ flags: CollectionFlags,
) -> Result<CollectionId, DispatchError> {
- <PalletCommon<T>>::init_collection(owner, payer, data, CollectionFlags::default())
+ <PalletCommon<T>>::init_collection(owner, payer, data, flags)
}
/// Destroy RFT collection
pallets/unique/src/eth/mod.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//! Implementation of CollectionHelpers contract.1819use core::marker::PhantomData;20use ethereum as _;21use evm_coder::{execution::*, generate_stubgen, solidity_interface, solidity, weight, types::*};22use frame_support::traits::Get;23use pallet_common::{24 CollectionById,25 dispatch::CollectionDispatch,26 erc::{27 CollectionHelpersEvents,28 static_property::{key, value as property_value},29 },30 Pallet as PalletCommon,31};32use pallet_evm_coder_substrate::{dispatch_to_evm, SubstrateRecorder, WithRecorder};33use pallet_evm::{account::CrossAccountId, OnMethodCall, PrecompileHandle, PrecompileResult};34use sp_std::vec;35use up_data_structs::{36 CollectionName, CollectionDescription, CollectionTokenPrefix, CreateCollectionData,37 CollectionMode, PropertyValue,38};3940use crate::{Config, SelfWeightOf, weights::WeightInfo};4142use sp_std::vec::Vec;43use alloc::format;4445/// See [`CollectionHelpersCall`]46pub struct EvmCollectionHelpers<T: Config>(SubstrateRecorder<T>);47impl<T: Config> WithRecorder<T> for EvmCollectionHelpers<T> {48 fn recorder(&self) -> &SubstrateRecorder<T> {49 &self.050 }5152 fn into_recorder(self) -> SubstrateRecorder<T> {53 self.054 }55}5657fn convert_data<T: Config>(58 caller: caller,59 name: string,60 description: string,61 token_prefix: string,62 base_uri: string,63) -> Result<(64 T::CrossAccountId,65 CollectionName,66 CollectionDescription,67 CollectionTokenPrefix,68 PropertyValue,69)> {70 let caller = T::CrossAccountId::from_eth(caller);71 let name = name72 .encode_utf16()73 .collect::<Vec<u16>>()74 .try_into()75 .map_err(|_| error_field_too_long(stringify!(name), CollectionName::bound()))?;76 let description = description77 .encode_utf16()78 .collect::<Vec<u16>>()79 .try_into()80 .map_err(|_| {81 error_field_too_long(stringify!(description), CollectionDescription::bound())82 })?;83 let token_prefix = token_prefix.into_bytes().try_into().map_err(|_| {84 error_field_too_long(stringify!(token_prefix), CollectionTokenPrefix::bound())85 })?;86 let base_uri_value = base_uri87 .into_bytes()88 .try_into()89 .map_err(|_| error_field_too_long(stringify!(token_prefix), PropertyValue::bound()))?;90 Ok((caller, name, description, token_prefix, base_uri_value))91}9293fn default_url_pkp() -> up_data_structs::PropertyKeyPermission {94 up_data_structs::PropertyKeyPermission {95 key: key::url(),96 permission: up_data_structs::PropertyPermission {97 mutable: true,98 collection_admin: true,99 token_owner: false,100 },101 }102}103fn default_suffix_pkp() -> up_data_structs::PropertyKeyPermission {104 up_data_structs::PropertyKeyPermission {105 key: key::suffix(),106 permission: up_data_structs::PropertyPermission {107 mutable: true,108 collection_admin: true,109 token_owner: false,110 },111 }112}113fn make_data<T: Config>(114 name: CollectionName,115 mode: CollectionMode,116 description: CollectionDescription,117 token_prefix: CollectionTokenPrefix,118 base_uri_value: PropertyValue,119 add_properties: bool,120) -> Result<CreateCollectionData<T::AccountId>> {121 let token_property_permissions = if add_properties {122 vec![default_url_pkp(), default_suffix_pkp()]123 .try_into()124 .map_err(|e| Error::Revert(format!("{:?}", e)))?125 } else {126 up_data_structs::CollectionPropertiesPermissionsVec::default()127 };128 let properties = if add_properties {129 let mut properties = vec![130 up_data_structs::Property {131 key: key::schema_name(),132 value: property_value::erc721(),133 },134 up_data_structs::Property {135 key: key::schema_version(),136 value: property_value::schema_version(),137 },138 ];139 if !base_uri_value.is_empty() {140 properties.push(up_data_structs::Property {141 key: key::base_uri(),142 value: base_uri_value,143 })144 }145 properties146 .try_into()147 .map_err(|e| Error::Revert(format!("{:?}", e)))?148 } else {149 up_data_structs::CollectionPropertiesVec::default()150 };151152 let data = CreateCollectionData {153 name,154 mode,155 description,156 token_prefix,157 token_property_permissions,158 properties,159 ..Default::default()160 };161 Ok(data)162}163164fn create_refungible_collection_internal<165 T: Config + pallet_nonfungible::Config + pallet_refungible::Config,166>(167 caller: caller,168 value: value,169 name: string,170 description: string,171 token_prefix: string,172 base_uri: string,173 add_properties: bool,174) -> Result<address> {175 let (caller, name, description, token_prefix, base_uri_value) =176 convert_data::<T>(caller, name, description, token_prefix, base_uri)?;177 let data = make_data::<T>(178 name,179 CollectionMode::ReFungible,180 description,181 token_prefix,182 base_uri_value,183 add_properties,184 )?;185 check_sent_amount_equals_collection_creation_price::<T>(value)?;186 let collection_helpers_address =187 T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());188189 let collection_id =190 T::CollectionDispatch::create(caller.clone(), collection_helpers_address, data)191 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;192 let address = pallet_common::eth::collection_id_to_address(collection_id);193 Ok(address)194}195196fn check_sent_amount_equals_collection_creation_price<T: Config>(value: value) -> Result<()> {197 let value = value.as_u128();198 let creation_price: u128 = T::CollectionCreationPrice::get()199 .try_into()200 .map_err(|_| ()) // workaround for `expect` requiring `Debug` trait201 .expect("Collection creation price should be convertible to u128");202 if value != creation_price {203 return Err(format!(204 "Sent amount not equals to collection creation price ({0})",205 creation_price206 )207 .into());208 }209 Ok(())210}211212/// @title Contract, which allows users to operate with collections213#[solidity_interface(name = CollectionHelpers, events(CollectionHelpersEvents))]214impl<T> EvmCollectionHelpers<T>215where216 T: Config + pallet_common::Config + pallet_nonfungible::Config + pallet_refungible::Config,217{218 /// Create an NFT collection219 /// @param name Name of the collection220 /// @param description Informative description of the collection221 /// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications222 /// @return address Address of the newly created collection223 #[weight(<SelfWeightOf<T>>::create_collection())]224 #[solidity(rename_selector = "createNFTCollection")]225 fn create_nft_collection(226 &mut self,227 caller: caller,228 value: value,229 name: string,230 description: string,231 token_prefix: string,232 ) -> Result<address> {233 let (caller, name, description, token_prefix, _base_uri_value) =234 convert_data::<T>(caller, name, description, token_prefix, "".into())?;235 let data = make_data::<T>(236 name,237 CollectionMode::NFT,238 description,239 token_prefix,240 Default::default(),241 false,242 )?;243 check_sent_amount_equals_collection_creation_price::<T>(value)?;244 let collection_helpers_address =245 T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());246 let collection_id = T::CollectionDispatch::create(caller, collection_helpers_address, data)247 .map_err(dispatch_to_evm::<T>)?;248249 let address = pallet_common::eth::collection_id_to_address(collection_id);250 Ok(address)251 }252 /// Create an NFT collection253 /// @param name Name of the collection254 /// @param description Informative description of the collection255 /// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications256 /// @return address Address of the newly created collection257 #[weight(<SelfWeightOf<T>>::create_collection())]258 #[deprecated(note = "mathod was renamed to `create_nft_collection`, prefer it instead")]259 fn create_nonfungible_collection(260 &mut self,261 caller: caller,262 value: value,263 name: string,264 description: string,265 token_prefix: string,266 ) -> Result<address> {267 self.create_nft_collection(caller, value, name, description, token_prefix)268 }269270 #[weight(<SelfWeightOf<T>>::create_collection())]271 #[solidity(rename_selector = "createERC721MetadataCompatibleNFTCollection")]272 fn create_nonfungible_collection_with_properties(273 &mut self,274 caller: caller,275 value: value,276 name: string,277 description: string,278 token_prefix: string,279 base_uri: string,280 ) -> Result<address> {281 let (caller, name, description, token_prefix, base_uri_value) =282 convert_data::<T>(caller, name, description, token_prefix, base_uri)?;283 let data = make_data::<T>(284 name,285 CollectionMode::NFT,286 description,287 token_prefix,288 base_uri_value,289 true,290 )?;291 check_sent_amount_equals_collection_creation_price::<T>(value)?;292 let collection_helpers_address =293 T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());294 let collection_id = T::CollectionDispatch::create(caller, collection_helpers_address, data)295 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;296297 let address = pallet_common::eth::collection_id_to_address(collection_id);298 Ok(address)299 }300301 #[weight(<SelfWeightOf<T>>::create_collection())]302 #[solidity(rename_selector = "createRFTCollection")]303 fn create_rft_collection(304 &mut self,305 caller: caller,306 value: value,307 name: string,308 description: string,309 token_prefix: string,310 ) -> Result<address> {311 create_refungible_collection_internal::<T>(312 caller,313 value,314 name,315 description,316 token_prefix,317 Default::default(),318 false,319 )320 }321322 #[weight(<SelfWeightOf<T>>::create_collection())]323 #[solidity(rename_selector = "createERC721MetadataCompatibleRFTCollection")]324 fn create_refungible_collection_with_properties(325 &mut self,326 caller: caller,327 value: value,328 name: string,329 description: string,330 token_prefix: string,331 base_uri: string,332 ) -> Result<address> {333 create_refungible_collection_internal::<T>(334 caller,335 value,336 name,337 description,338 token_prefix,339 base_uri,340 true,341 )342 }343344 #[solidity(rename_selector = "makeCollectionERC721MetadataCompatible")]345 fn make_collection_metadata_compatible(346 &mut self,347 caller: caller,348 collection: address,349 base_uri: string,350 ) -> Result<()> {351 let caller = T::CrossAccountId::from_eth(caller);352 let collection =353 pallet_common::eth::map_eth_to_id(&collection).ok_or("not a collection address")?;354 let mut collection =355 <crate::CollectionHandle<T>>::new(collection).ok_or("collection not found")?;356357 if !matches!(358 collection.mode,359 CollectionMode::NFT | CollectionMode::ReFungible360 ) {361 return Err("target collection should be either NFT or Refungible".into());362 }363364 self.recorder().consume_sstore()?;365 collection366 .check_is_owner_or_admin(&caller)367 .map_err(dispatch_to_evm::<T>)?;368369 if collection.flags.erc721metadata {370 return Err("target collection is already Erc721Metadata compatible".into());371 }372 collection.flags.erc721metadata = true;373374 let all_permissions = <pallet_common::CollectionPropertyPermissions<T>>::get(collection.id);375 if all_permissions.get(&key::url()).is_none() {376 self.recorder().consume_sstore()?;377 <PalletCommon<T>>::set_property_permission(&collection, &caller, default_url_pkp())378 .map_err(dispatch_to_evm::<T>)?;379 }380 if all_permissions.get(&key::suffix()).is_none() {381 self.recorder().consume_sstore()?;382 <PalletCommon<T>>::set_property_permission(&collection, &caller, default_suffix_pkp())383 .map_err(dispatch_to_evm::<T>)?;384 }385386 let all_properties = <pallet_common::CollectionProperties<T>>::get(collection.id);387 let mut new_properties = vec![];388 if all_properties.get(&key::schema_name()).is_none() {389 self.recorder().consume_sstore()?;390 new_properties.push(up_data_structs::Property {391 key: key::schema_name(),392 value: property_value::erc721(),393 });394 new_properties.push(up_data_structs::Property {395 key: key::schema_version(),396 value: property_value::schema_version(),397 });398 }399 if all_properties.get(&key::base_uri()).is_none() && !base_uri.is_empty() {400 new_properties.push(up_data_structs::Property {401 key: key::base_uri(),402 value: base_uri403 .into_bytes()404 .try_into()405 .map_err(|_| "base uri is too large")?,406 });407 }408409 if !new_properties.is_empty() {410 self.recorder().consume_sstore()?;411 <PalletCommon<T>>::set_collection_properties(&collection, &caller, new_properties)412 .map_err(dispatch_to_evm::<T>)?;413 }414415 self.recorder().consume_sstore()?;416 collection.save().map_err(dispatch_to_evm::<T>)?;417418 Ok(())419 }420421 /// Check if a collection exists422 /// @param collectionAddress Address of the collection in question423 /// @return bool Does the collection exist?424 fn is_collection_exist(&self, _caller: caller, collection_address: address) -> Result<bool> {425 if let Some(id) = pallet_common::eth::map_eth_to_id(&collection_address) {426 let collection_id = id;427 return Ok(<CollectionById<T>>::contains_key(collection_id));428 }429430 Ok(false)431 }432433 fn collection_creation_fee(&self) -> Result<value> {434 let price: u128 = T::CollectionCreationPrice::get()435 .try_into()436 .map_err(|_| ()) // workaround for `expect` requiring `Debug` trait437 .expect("Collection creation price should be convertible to u128");438 Ok(price.into())439 }440}441442/// Implements [`OnMethodCall`], which delegates call to [`EvmCollectionHelpers`]443pub struct CollectionHelpersOnMethodCall<T: Config>(PhantomData<*const T>);444impl<T: Config + pallet_nonfungible::Config + pallet_refungible::Config> OnMethodCall<T>445 for CollectionHelpersOnMethodCall<T>446{447 fn is_reserved(contract: &sp_core::H160) -> bool {448 contract == &T::ContractAddress::get()449 }450451 fn is_used(contract: &sp_core::H160) -> bool {452 contract == &T::ContractAddress::get()453 }454455 fn call(handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {456 if handle.code_address() != T::ContractAddress::get() {457 return None;458 }459460 let helpers =461 EvmCollectionHelpers::<T>(SubstrateRecorder::<T>::new(handle.remaining_gas()));462 pallet_evm_coder_substrate::call(handle, helpers)463 }464465 fn get_code(contract: &sp_core::H160) -> Option<Vec<u8>> {466 (contract == &T::ContractAddress::get())467 .then(|| include_bytes!("./stubs/CollectionHelpers.raw").to_vec())468 }469}470471generate_stubgen!(collection_helper_impl, CollectionHelpersCall<()>, true);472generate_stubgen!(collection_helper_iface, CollectionHelpersCall<()>, false);473474fn error_field_too_long(feild: &str, bound: usize) -> Error {475 Error::Revert(format!("{} is too long. Max length is {}.", feild, bound))476}1// 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//! Implementation of CollectionHelpers contract.1819use core::marker::PhantomData;20use ethereum as _;21use evm_coder::{execution::*, generate_stubgen, solidity_interface, solidity, weight, types::*};22use frame_support::traits::Get;23use pallet_common::{24 CollectionById,25 dispatch::CollectionDispatch,26 erc::{27 CollectionHelpersEvents,28 static_property::{key, value as property_value},29 },30 Pallet as PalletCommon,31};32use pallet_evm_coder_substrate::{dispatch_to_evm, SubstrateRecorder, WithRecorder};33use pallet_evm::{account::CrossAccountId, OnMethodCall, PrecompileHandle, PrecompileResult};34use sp_std::vec;35use up_data_structs::{36 CollectionName, CollectionDescription, CollectionTokenPrefix, CreateCollectionData,37 CollectionMode, PropertyValue, CollectionFlags,38};3940use crate::{Config, SelfWeightOf, weights::WeightInfo};4142use sp_std::vec::Vec;43use alloc::format;4445/// See [`CollectionHelpersCall`]46pub struct EvmCollectionHelpers<T: Config>(SubstrateRecorder<T>);47impl<T: Config> WithRecorder<T> for EvmCollectionHelpers<T> {48 fn recorder(&self) -> &SubstrateRecorder<T> {49 &self.050 }5152 fn into_recorder(self) -> SubstrateRecorder<T> {53 self.054 }55}5657fn convert_data<T: Config>(58 caller: caller,59 name: string,60 description: string,61 token_prefix: string,62 base_uri: string,63) -> Result<(64 T::CrossAccountId,65 CollectionName,66 CollectionDescription,67 CollectionTokenPrefix,68 PropertyValue,69)> {70 let caller = T::CrossAccountId::from_eth(caller);71 let name = name72 .encode_utf16()73 .collect::<Vec<u16>>()74 .try_into()75 .map_err(|_| error_field_too_long(stringify!(name), CollectionName::bound()))?;76 let description = description77 .encode_utf16()78 .collect::<Vec<u16>>()79 .try_into()80 .map_err(|_| {81 error_field_too_long(stringify!(description), CollectionDescription::bound())82 })?;83 let token_prefix = token_prefix.into_bytes().try_into().map_err(|_| {84 error_field_too_long(stringify!(token_prefix), CollectionTokenPrefix::bound())85 })?;86 let base_uri_value = base_uri87 .into_bytes()88 .try_into()89 .map_err(|_| error_field_too_long(stringify!(token_prefix), PropertyValue::bound()))?;90 Ok((caller, name, description, token_prefix, base_uri_value))91}9293fn default_url_pkp() -> up_data_structs::PropertyKeyPermission {94 up_data_structs::PropertyKeyPermission {95 key: key::url(),96 permission: up_data_structs::PropertyPermission {97 mutable: true,98 collection_admin: true,99 token_owner: false,100 },101 }102}103fn default_suffix_pkp() -> up_data_structs::PropertyKeyPermission {104 up_data_structs::PropertyKeyPermission {105 key: key::suffix(),106 permission: up_data_structs::PropertyPermission {107 mutable: true,108 collection_admin: true,109 token_owner: false,110 },111 }112}113fn make_data<T: Config>(114 name: CollectionName,115 mode: CollectionMode,116 description: CollectionDescription,117 token_prefix: CollectionTokenPrefix,118 base_uri_value: PropertyValue,119 add_properties: bool,120) -> Result<CreateCollectionData<T::AccountId>> {121 let token_property_permissions = if add_properties {122 vec![default_url_pkp(), default_suffix_pkp()]123 .try_into()124 .map_err(|e| Error::Revert(format!("{:?}", e)))?125 } else {126 up_data_structs::CollectionPropertiesPermissionsVec::default()127 };128 let properties = if add_properties {129 let mut properties = vec![130 up_data_structs::Property {131 key: key::schema_name(),132 value: property_value::erc721(),133 },134 up_data_structs::Property {135 key: key::schema_version(),136 value: property_value::schema_version(),137 },138 ];139 if !base_uri_value.is_empty() {140 properties.push(up_data_structs::Property {141 key: key::base_uri(),142 value: base_uri_value,143 })144 }145 properties146 .try_into()147 .map_err(|e| Error::Revert(format!("{:?}", e)))?148 } else {149 up_data_structs::CollectionPropertiesVec::default()150 };151152 let data = CreateCollectionData {153 name,154 mode,155 description,156 token_prefix,157 token_property_permissions,158 properties,159 ..Default::default()160 };161 Ok(data)162}163164fn create_refungible_collection_internal<165 T: Config + pallet_nonfungible::Config + pallet_refungible::Config,166>(167 caller: caller,168 value: value,169 name: string,170 description: string,171 token_prefix: string,172 base_uri: string,173 add_properties: bool,174) -> Result<address> {175 let (caller, name, description, token_prefix, base_uri_value) =176 convert_data::<T>(caller, name, description, token_prefix, base_uri)?;177 let data = make_data::<T>(178 name,179 CollectionMode::ReFungible,180 description,181 token_prefix,182 base_uri_value,183 add_properties,184 )?;185 check_sent_amount_equals_collection_creation_price::<T>(value)?;186 let collection_helpers_address =187 T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());188189 let collection_id = T::CollectionDispatch::create(190 caller.clone(),191 collection_helpers_address,192 data,193 CollectionFlags {194 erc721metadata: add_properties,195 ..Default::default()196 },197 )198 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;199 let address = pallet_common::eth::collection_id_to_address(collection_id);200 Ok(address)201}202203fn check_sent_amount_equals_collection_creation_price<T: Config>(value: value) -> Result<()> {204 let value = value.as_u128();205 let creation_price: u128 = T::CollectionCreationPrice::get()206 .try_into()207 .map_err(|_| ()) // workaround for `expect` requiring `Debug` trait208 .expect("Collection creation price should be convertible to u128");209 if value != creation_price {210 return Err(format!(211 "Sent amount not equals to collection creation price ({0})",212 creation_price213 )214 .into());215 }216 Ok(())217}218219/// @title Contract, which allows users to operate with collections220#[solidity_interface(name = CollectionHelpers, events(CollectionHelpersEvents))]221impl<T> EvmCollectionHelpers<T>222where223 T: Config + pallet_common::Config + pallet_nonfungible::Config + pallet_refungible::Config,224{225 /// Create an NFT collection226 /// @param name Name of the collection227 /// @param description Informative description of the collection228 /// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications229 /// @return address Address of the newly created collection230 #[weight(<SelfWeightOf<T>>::create_collection())]231 #[solidity(rename_selector = "createNFTCollection")]232 fn create_nft_collection(233 &mut self,234 caller: caller,235 value: value,236 name: string,237 description: string,238 token_prefix: string,239 ) -> Result<address> {240 let (caller, name, description, token_prefix, _base_uri_value) =241 convert_data::<T>(caller, name, description, token_prefix, "".into())?;242 let data = make_data::<T>(243 name,244 CollectionMode::NFT,245 description,246 token_prefix,247 Default::default(),248 false,249 )?;250 check_sent_amount_equals_collection_creation_price::<T>(value)?;251 let collection_helpers_address =252 T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());253 let collection_id = T::CollectionDispatch::create(254 caller,255 collection_helpers_address,256 data,257 Default::default(),258 )259 .map_err(dispatch_to_evm::<T>)?;260261 let address = pallet_common::eth::collection_id_to_address(collection_id);262 Ok(address)263 }264 /// Create an NFT collection265 /// @param name Name of the collection266 /// @param description Informative description of the collection267 /// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications268 /// @return address Address of the newly created collection269 #[weight(<SelfWeightOf<T>>::create_collection())]270 #[deprecated(note = "mathod was renamed to `create_nft_collection`, prefer it instead")]271 fn create_nonfungible_collection(272 &mut self,273 caller: caller,274 value: value,275 name: string,276 description: string,277 token_prefix: string,278 ) -> Result<address> {279 self.create_nft_collection(caller, value, name, description, token_prefix)280 }281282 #[weight(<SelfWeightOf<T>>::create_collection())]283 #[solidity(rename_selector = "createERC721MetadataCompatibleNFTCollection")]284 fn create_nonfungible_collection_with_properties(285 &mut self,286 caller: caller,287 value: value,288 name: string,289 description: string,290 token_prefix: string,291 base_uri: string,292 ) -> Result<address> {293 let (caller, name, description, token_prefix, base_uri_value) =294 convert_data::<T>(caller, name, description, token_prefix, base_uri)?;295 let data = make_data::<T>(296 name,297 CollectionMode::NFT,298 description,299 token_prefix,300 base_uri_value,301 true,302 )?;303 check_sent_amount_equals_collection_creation_price::<T>(value)?;304 let collection_helpers_address =305 T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());306 let collection_id = T::CollectionDispatch::create(307 caller,308 collection_helpers_address,309 data,310 CollectionFlags {311 erc721metadata: true,312 ..Default::default()313 },314 )315 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;316317 let address = pallet_common::eth::collection_id_to_address(collection_id);318 Ok(address)319 }320321 #[weight(<SelfWeightOf<T>>::create_collection())]322 #[solidity(rename_selector = "createRFTCollection")]323 fn create_rft_collection(324 &mut self,325 caller: caller,326 value: value,327 name: string,328 description: string,329 token_prefix: string,330 ) -> Result<address> {331 create_refungible_collection_internal::<T>(332 caller,333 value,334 name,335 description,336 token_prefix,337 Default::default(),338 false,339 )340 }341342 #[weight(<SelfWeightOf<T>>::create_collection())]343 #[solidity(rename_selector = "createERC721MetadataCompatibleRFTCollection")]344 fn create_refungible_collection_with_properties(345 &mut self,346 caller: caller,347 value: value,348 name: string,349 description: string,350 token_prefix: string,351 base_uri: string,352 ) -> Result<address> {353 create_refungible_collection_internal::<T>(354 caller,355 value,356 name,357 description,358 token_prefix,359 base_uri,360 true,361 )362 }363364 #[solidity(rename_selector = "makeCollectionERC721MetadataCompatible")]365 fn make_collection_metadata_compatible(366 &mut self,367 caller: caller,368 collection: address,369 base_uri: string,370 ) -> Result<()> {371 let caller = T::CrossAccountId::from_eth(caller);372 let collection =373 pallet_common::eth::map_eth_to_id(&collection).ok_or("not a collection address")?;374 let mut collection =375 <crate::CollectionHandle<T>>::new(collection).ok_or("collection not found")?;376377 if !matches!(378 collection.mode,379 CollectionMode::NFT | CollectionMode::ReFungible380 ) {381 return Err("target collection should be either NFT or Refungible".into());382 }383384 self.recorder().consume_sstore()?;385 collection386 .check_is_owner_or_admin(&caller)387 .map_err(dispatch_to_evm::<T>)?;388389 if collection.flags.erc721metadata {390 return Err("target collection is already Erc721Metadata compatible".into());391 }392 collection.flags.erc721metadata = true;393394 let all_permissions = <pallet_common::CollectionPropertyPermissions<T>>::get(collection.id);395 if all_permissions.get(&key::url()).is_none() {396 self.recorder().consume_sstore()?;397 <PalletCommon<T>>::set_property_permission(&collection, &caller, default_url_pkp())398 .map_err(dispatch_to_evm::<T>)?;399 }400 if all_permissions.get(&key::suffix()).is_none() {401 self.recorder().consume_sstore()?;402 <PalletCommon<T>>::set_property_permission(&collection, &caller, default_suffix_pkp())403 .map_err(dispatch_to_evm::<T>)?;404 }405406 let all_properties = <pallet_common::CollectionProperties<T>>::get(collection.id);407 let mut new_properties = vec![];408 if all_properties.get(&key::schema_name()).is_none() {409 self.recorder().consume_sstore()?;410 new_properties.push(up_data_structs::Property {411 key: key::schema_name(),412 value: property_value::erc721(),413 });414 new_properties.push(up_data_structs::Property {415 key: key::schema_version(),416 value: property_value::schema_version(),417 });418 }419 if all_properties.get(&key::base_uri()).is_none() && !base_uri.is_empty() {420 new_properties.push(up_data_structs::Property {421 key: key::base_uri(),422 value: base_uri423 .into_bytes()424 .try_into()425 .map_err(|_| "base uri is too large")?,426 });427 }428429 if !new_properties.is_empty() {430 self.recorder().consume_sstore()?;431 <PalletCommon<T>>::set_collection_properties(&collection, &caller, new_properties)432 .map_err(dispatch_to_evm::<T>)?;433 }434435 self.recorder().consume_sstore()?;436 collection.save().map_err(dispatch_to_evm::<T>)?;437438 Ok(())439 }440441 /// Check if a collection exists442 /// @param collectionAddress Address of the collection in question443 /// @return bool Does the collection exist?444 fn is_collection_exist(&self, _caller: caller, collection_address: address) -> Result<bool> {445 if let Some(id) = pallet_common::eth::map_eth_to_id(&collection_address) {446 let collection_id = id;447 return Ok(<CollectionById<T>>::contains_key(collection_id));448 }449450 Ok(false)451 }452453 fn collection_creation_fee(&self) -> Result<value> {454 let price: u128 = T::CollectionCreationPrice::get()455 .try_into()456 .map_err(|_| ()) // workaround for `expect` requiring `Debug` trait457 .expect("Collection creation price should be convertible to u128");458 Ok(price.into())459 }460}461462/// Implements [`OnMethodCall`], which delegates call to [`EvmCollectionHelpers`]463pub struct CollectionHelpersOnMethodCall<T: Config>(PhantomData<*const T>);464impl<T: Config + pallet_nonfungible::Config + pallet_refungible::Config> OnMethodCall<T>465 for CollectionHelpersOnMethodCall<T>466{467 fn is_reserved(contract: &sp_core::H160) -> bool {468 contract == &T::ContractAddress::get()469 }470471 fn is_used(contract: &sp_core::H160) -> bool {472 contract == &T::ContractAddress::get()473 }474475 fn call(handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {476 if handle.code_address() != T::ContractAddress::get() {477 return None;478 }479480 let helpers =481 EvmCollectionHelpers::<T>(SubstrateRecorder::<T>::new(handle.remaining_gas()));482 pallet_evm_coder_substrate::call(handle, helpers)483 }484485 fn get_code(contract: &sp_core::H160) -> Option<Vec<u8>> {486 (contract == &T::ContractAddress::get())487 .then(|| include_bytes!("./stubs/CollectionHelpers.raw").to_vec())488 }489}490491generate_stubgen!(collection_helper_impl, CollectionHelpersCall<()>, true);492generate_stubgen!(collection_helper_iface, CollectionHelpersCall<()>, false);493494fn error_field_too_long(feild: &str, bound: usize) -> Error {495 Error::Revert(format!("{} is too long. Max length is {}.", feild, bound))496}pallets/unique/src/lib.rsdiffbeforeafterboth--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -345,7 +345,7 @@
// =========
let sender = T::CrossAccountId::from_sub(sender);
- let _id = T::CollectionDispatch::create(sender.clone(), sender, data)?;
+ let _id = T::CollectionDispatch::create(sender.clone(), sender, data, Default::default())?;
Ok(())
}
runtime/common/dispatch.rsdiffbeforeafterboth--- a/runtime/common/dispatch.rs
+++ b/runtime/common/dispatch.rs
@@ -31,7 +31,7 @@
};
use up_data_structs::{
CollectionMode, CreateCollectionData, MAX_DECIMAL_POINTS, mapping::TokenAddressMapping,
- CollectionId,
+ CollectionId, CollectionFlags,
};
#[cfg(not(feature = "refungible"))]
@@ -57,10 +57,11 @@
sender: T::CrossAccountId,
payer: T::CrossAccountId,
data: CreateCollectionData<T::AccountId>,
+ flags: CollectionFlags,
) -> Result<CollectionId, DispatchError> {
let id = match data.mode {
CollectionMode::NFT => {
- <PalletNonfungible<T>>::init_collection(sender, payer, data, false)?
+ <PalletNonfungible<T>>::init_collection(sender, payer, data, flags)?
}
CollectionMode::Fungible(decimal_points) => {
// check params
@@ -68,11 +69,13 @@
decimal_points <= MAX_DECIMAL_POINTS,
pallet_unique::Error::<T>::CollectionDecimalPointLimitExceeded
);
- <PalletFungible<T>>::init_collection(sender, payer, data)?
+ <PalletFungible<T>>::init_collection(sender, payer, data, flags)?
}
#[cfg(feature = "refungible")]
- CollectionMode::ReFungible => <PalletRefungible<T>>::init_collection(sender, payer, data)?,
+ CollectionMode::ReFungible => {
+ <PalletRefungible<T>>::init_collection(sender, payer, data, flags)?
+ }
#[cfg(not(feature = "refungible"))]
CollectionMode::ReFungible => return unsupported!(T),