difftreelog
add `collectionComtractAddress` funtion to `CollectionHelpers` interface
in: master
13 files changed
pallets/fungible/src/stubs/UniqueFungible.rawdiffbeforeafterbothbinary blob — no preview
pallets/nonfungible/src/erc.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -490,8 +490,8 @@
// TODO: Not implemetable
Err("not implemented".into())
}
-
- /// @notice Returns collection helper contract address
+
+ /// @notice Returns collection helper contract address
fn collection_helper_address(&self) -> Result<address> {
Ok(T::ContractAddress::get())
}
pallets/nonfungible/src/stubs/UniqueNFT.rawdiffbeforeafterbothbinary blob — no preview
pallets/refungible/src/erc.rsdiffbeforeafterboth--- a/pallets/refungible/src/erc.rs
+++ b/pallets/refungible/src/erc.rs
@@ -482,7 +482,7 @@
// TODO: Not implemetable
Err("not implemented".into())
}
-
+
/// @notice Returns collection helper contract address
fn collection_helper_address(&self) -> Result<address> {
Ok(T::ContractAddress::get())
pallets/refungible/src/stubs/UniqueRefungible.rawdiffbeforeafterbothbinary blob — no preview
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::{22 abi::AbiType, execution::*, generate_stubgen, solidity, solidity_interface, types::*, weight,23};24use frame_support::traits::Get;25use crate::Pallet;2627use pallet_common::{28 CollectionById,29 dispatch::CollectionDispatch,30 erc::{CollectionHelpersEvents, static_property::key},31 Pallet as PalletCommon,32};33use pallet_evm::{account::CrossAccountId, OnMethodCall, PrecompileHandle, PrecompileResult};34use pallet_evm_coder_substrate::{dispatch_to_evm, SubstrateRecorder, WithRecorder};35use sp_std::vec;36use up_data_structs::{37 CollectionDescription, CollectionMode, CollectionName, CollectionTokenPrefix,38 CreateCollectionData,39};4041use crate::{weights::WeightInfo, Config, SelfWeightOf};4243use alloc::format;44use sp_std::vec::Vec;4546/// See [`CollectionHelpersCall`]47pub struct EvmCollectionHelpers<T: Config>(SubstrateRecorder<T>);48impl<T: Config> WithRecorder<T> for EvmCollectionHelpers<T> {49 fn recorder(&self) -> &SubstrateRecorder<T> {50 &self.051 }5253 fn into_recorder(self) -> SubstrateRecorder<T> {54 self.055 }56}5758fn convert_data<T: Config>(59 caller: caller,60 name: string,61 description: string,62 token_prefix: string,63) -> Result<(64 T::CrossAccountId,65 CollectionName,66 CollectionDescription,67 CollectionTokenPrefix,68)> {69 let caller = T::CrossAccountId::from_eth(caller);70 let name = name71 .encode_utf16()72 .collect::<Vec<u16>>()73 .try_into()74 .map_err(|_| error_field_too_long(stringify!(name), CollectionName::bound()))?;75 let description = description76 .encode_utf16()77 .collect::<Vec<u16>>()78 .try_into()79 .map_err(|_| {80 error_field_too_long(stringify!(description), CollectionDescription::bound())81 })?;82 let token_prefix = token_prefix.into_bytes().try_into().map_err(|_| {83 error_field_too_long(stringify!(token_prefix), CollectionTokenPrefix::bound())84 })?;85 Ok((caller, name, description, token_prefix))86}8788#[inline(always)]89fn create_collection_internal<T: Config>(90 caller: caller,91 value: value,92 name: string,93 collection_mode: CollectionMode,94 description: string,95 token_prefix: string,96) -> Result<address> {97 let (caller, name, description, token_prefix) =98 convert_data::<T>(caller, name, description, token_prefix)?;99 let data = CreateCollectionData {100 name,101 mode: collection_mode,102 description,103 token_prefix,104 ..Default::default()105 };106 check_sent_amount_equals_collection_creation_price::<T>(value)?;107 let collection_helpers_address =108 T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());109110 let collection_id = T::CollectionDispatch::create(111 caller.clone(),112 collection_helpers_address,113 data,114 Default::default(),115 )116 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;117 let address = pallet_common::eth::collection_id_to_address(collection_id);118 Ok(address)119}120121fn check_sent_amount_equals_collection_creation_price<T: Config>(value: value) -> Result<()> {122 let value = value.as_u128();123 let creation_price: u128 = T::CollectionCreationPrice::get()124 .try_into()125 .map_err(|_| ()) // workaround for `expect` requiring `Debug` trait126 .expect("Collection creation price should be convertible to u128");127 if value != creation_price {128 return Err(format!(129 "Sent amount not equals to collection creation price ({0})",130 creation_price131 )132 .into());133 }134 Ok(())135}136137/// @title Contract, which allows users to operate with collections138#[solidity_interface(name = CollectionHelpers, events(CollectionHelpersEvents))]139impl<T> EvmCollectionHelpers<T>140where141 T: Config + pallet_common::Config + pallet_nonfungible::Config + pallet_refungible::Config,142{143 /// Create an NFT collection144 /// @param name Name of the collection145 /// @param description Informative description of the collection146 /// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications147 /// @return address Address of the newly created collection148 #[weight(<SelfWeightOf<T>>::create_collection())]149 #[solidity(rename_selector = "createNFTCollection")]150 fn create_nft_collection(151 &mut self,152 caller: caller,153 value: value,154 name: string,155 description: string,156 token_prefix: string,157 ) -> Result<address> {158 let (caller, name, description, token_prefix) =159 convert_data::<T>(caller, name, description, token_prefix)?;160 let data = CreateCollectionData {161 name,162 mode: CollectionMode::NFT,163 description,164 token_prefix,165 ..Default::default()166 };167 check_sent_amount_equals_collection_creation_price::<T>(value)?;168 let collection_helpers_address =169 T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());170 let collection_id = T::CollectionDispatch::create(171 caller,172 collection_helpers_address,173 data,174 Default::default(),175 )176 .map_err(dispatch_to_evm::<T>)?;177178 let address = pallet_common::eth::collection_id_to_address(collection_id);179 Ok(address)180 }181 /// Create an NFT collection182 /// @param name Name of the collection183 /// @param description Informative description of the collection184 /// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications185 /// @return address Address of the newly created collection186 #[weight(<SelfWeightOf<T>>::create_collection())]187 #[deprecated(note = "mathod was renamed to `create_nft_collection`, prefer it instead")]188 #[solidity(hide)]189 fn create_nonfungible_collection(190 &mut self,191 caller: caller,192 value: value,193 name: string,194 description: string,195 token_prefix: string,196 ) -> Result<address> {197 create_collection_internal::<T>(198 caller,199 value,200 name,201 CollectionMode::NFT,202 description,203 token_prefix,204 )205 }206207 #[weight(<SelfWeightOf<T>>::create_collection())]208 #[solidity(rename_selector = "createRFTCollection")]209 fn create_rft_collection(210 &mut self,211 caller: caller,212 value: value,213 name: string,214 description: string,215 token_prefix: string,216 ) -> Result<address> {217 create_collection_internal::<T>(218 caller,219 value,220 name,221 CollectionMode::ReFungible,222 description,223 token_prefix,224 )225 }226227 #[weight(<SelfWeightOf<T>>::create_collection())]228 #[solidity(rename_selector = "createFTCollection")]229 fn create_fungible_collection(230 &mut self,231 caller: caller,232 value: value,233 name: string,234 decimals: uint8,235 description: string,236 token_prefix: string,237 ) -> Result<address> {238 create_collection_internal::<T>(239 caller,240 value,241 name,242 CollectionMode::Fungible(decimals),243 description,244 token_prefix,245 )246 }247248 #[solidity(rename_selector = "makeCollectionERC721MetadataCompatible")]249 fn make_collection_metadata_compatible(250 &mut self,251 caller: caller,252 collection: address,253 base_uri: string,254 ) -> Result<()> {255 let caller = T::CrossAccountId::from_eth(caller);256 let collection =257 pallet_common::eth::map_eth_to_id(&collection).ok_or("not a collection address")?;258 let mut collection =259 <crate::CollectionHandle<T>>::new(collection).ok_or("collection not found")?;260261 if !matches!(262 collection.mode,263 CollectionMode::NFT | CollectionMode::ReFungible264 ) {265 return Err("target collection should be either NFT or Refungible".into());266 }267268 self.recorder().consume_sstore()?;269 collection270 .check_is_owner_or_admin(&caller)271 .map_err(dispatch_to_evm::<T>)?;272273 if collection.flags.erc721metadata {274 return Err("target collection is already Erc721Metadata compatible".into());275 }276 collection.flags.erc721metadata = true;277278 let all_permissions = <pallet_common::CollectionPropertyPermissions<T>>::get(collection.id);279 if all_permissions.get(&key::url()).is_none() {280 self.recorder().consume_sstore()?;281 <PalletCommon<T>>::set_property_permission(282 &collection,283 &caller,284 up_data_structs::PropertyKeyPermission {285 key: key::url(),286 permission: up_data_structs::PropertyPermission {287 mutable: true,288 collection_admin: true,289 token_owner: false,290 },291 },292 )293 .map_err(dispatch_to_evm::<T>)?;294 }295 if all_permissions.get(&key::suffix()).is_none() {296 self.recorder().consume_sstore()?;297 <PalletCommon<T>>::set_property_permission(298 &collection,299 &caller,300 up_data_structs::PropertyKeyPermission {301 key: key::suffix(),302 permission: up_data_structs::PropertyPermission {303 mutable: true,304 collection_admin: true,305 token_owner: false,306 },307 },308 )309 .map_err(dispatch_to_evm::<T>)?;310 }311312 let all_properties = <pallet_common::CollectionProperties<T>>::get(collection.id);313 if all_properties.get(&key::base_uri()).is_none() && !base_uri.is_empty() {314 self.recorder().consume_sstore()?;315 <PalletCommon<T>>::set_collection_properties(316 &collection,317 &caller,318 vec![up_data_structs::Property {319 key: key::base_uri(),320 value: base_uri321 .into_bytes()322 .try_into()323 .map_err(|_| "base uri is too large")?,324 }],325 )326 .map_err(dispatch_to_evm::<T>)?;327 }328329 self.recorder().consume_sstore()?;330 collection.save().map_err(dispatch_to_evm::<T>)?;331332 Ok(())333 }334335 #[weight(<SelfWeightOf<T>>::destroy_collection())]336 fn destroy_collection(&mut self, caller: caller, collection_address: address) -> Result<void> {337 let caller = T::CrossAccountId::from_eth(caller);338339 let collection_id = pallet_common::eth::map_eth_to_id(&collection_address)340 .ok_or("Invalid collection address format")?;341 <Pallet<T>>::destroy_collection_internal(caller, collection_id)342 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)343 }344345 /// Check if a collection exists346 /// @param collectionAddress Address of the collection in question347 /// @return bool Does the collection exist?348 fn is_collection_exist(&self, _caller: caller, collection_address: address) -> Result<bool> {349 if let Some(id) = pallet_common::eth::map_eth_to_id(&collection_address) {350 let collection_id = id;351 return Ok(<CollectionById<T>>::contains_key(collection_id));352 }353354 Ok(false)355 }356357 fn collection_creation_fee(&self) -> Result<value> {358 let price: u128 = T::CollectionCreationPrice::get()359 .try_into()360 .map_err(|_| ()) // workaround for `expect` requiring `Debug` trait361 .expect("Collection creation price should be convertible to u128");362 Ok(price.into())363 }364}365366/// Implements [`OnMethodCall`], which delegates call to [`EvmCollectionHelpers`]367pub struct CollectionHelpersOnMethodCall<T: Config>(PhantomData<*const T>);368impl<T: Config + pallet_nonfungible::Config + pallet_refungible::Config> OnMethodCall<T>369 for CollectionHelpersOnMethodCall<T>370{371 fn is_reserved(contract: &sp_core::H160) -> bool {372 contract == &T::ContractAddress::get()373 }374375 fn is_used(contract: &sp_core::H160) -> bool {376 contract == &T::ContractAddress::get()377 }378379 fn call(handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {380 if handle.code_address() != T::ContractAddress::get() {381 return None;382 }383384 let helpers =385 EvmCollectionHelpers::<T>(SubstrateRecorder::<T>::new(handle.remaining_gas()));386 pallet_evm_coder_substrate::call(handle, helpers)387 }388389 fn get_code(contract: &sp_core::H160) -> Option<Vec<u8>> {390 (contract == &T::ContractAddress::get())391 .then(|| include_bytes!("./stubs/CollectionHelpers.raw").to_vec())392 }393}394395generate_stubgen!(collection_helper_impl, CollectionHelpersCall<()>, true);396generate_stubgen!(collection_helper_iface, CollectionHelpersCall<()>, false);397398fn error_field_too_long(feild: &str, bound: usize) -> Error {399 Error::Revert(format!("{} is too long. Max length is {}.", feild, bound))400}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::{22 abi::AbiType, execution::*, generate_stubgen, solidity, solidity_interface, types::*, weight,23};24use frame_support::traits::Get;25use crate::Pallet;2627use pallet_common::{28 CollectionById,29 dispatch::CollectionDispatch,30 erc::{CollectionHelpersEvents, static_property::key},31 eth::{map_eth_to_id, collection_id_to_address},32 Pallet as PalletCommon,33};34use pallet_evm::{account::CrossAccountId, OnMethodCall, PrecompileHandle, PrecompileResult};35use pallet_evm_coder_substrate::{dispatch_to_evm, SubstrateRecorder, WithRecorder};36use sp_std::vec;37use up_data_structs::{38 CollectionDescription, CollectionMode, CollectionName, CollectionTokenPrefix,39 CreateCollectionData, CollectionId,40};4142use crate::{weights::WeightInfo, Config, SelfWeightOf};4344use alloc::format;45use sp_std::vec::Vec;4647/// See [`CollectionHelpersCall`]48pub struct EvmCollectionHelpers<T: Config>(SubstrateRecorder<T>);49impl<T: Config> WithRecorder<T> for EvmCollectionHelpers<T> {50 fn recorder(&self) -> &SubstrateRecorder<T> {51 &self.052 }5354 fn into_recorder(self) -> SubstrateRecorder<T> {55 self.056 }57}5859fn convert_data<T: Config>(60 caller: caller,61 name: string,62 description: string,63 token_prefix: string,64) -> Result<(65 T::CrossAccountId,66 CollectionName,67 CollectionDescription,68 CollectionTokenPrefix,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 Ok((caller, name, description, token_prefix))87}8889#[inline(always)]90fn create_collection_internal<T: Config>(91 caller: caller,92 value: value,93 name: string,94 collection_mode: CollectionMode,95 description: string,96 token_prefix: string,97) -> Result<address> {98 let (caller, name, description, token_prefix) =99 convert_data::<T>(caller, name, description, token_prefix)?;100 let data = CreateCollectionData {101 name,102 mode: collection_mode,103 description,104 token_prefix,105 ..Default::default()106 };107 check_sent_amount_equals_collection_creation_price::<T>(value)?;108 let collection_helpers_address =109 T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());110111 let collection_id = T::CollectionDispatch::create(112 caller.clone(),113 collection_helpers_address,114 data,115 Default::default(),116 )117 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;118 let address = pallet_common::eth::collection_id_to_address(collection_id);119 Ok(address)120}121122fn check_sent_amount_equals_collection_creation_price<T: Config>(value: value) -> Result<()> {123 let value = value.as_u128();124 let creation_price: u128 = T::CollectionCreationPrice::get()125 .try_into()126 .map_err(|_| ()) // workaround for `expect` requiring `Debug` trait127 .expect("Collection creation price should be convertible to u128");128 if value != creation_price {129 return Err(format!(130 "Sent amount not equals to collection creation price ({0})",131 creation_price132 )133 .into());134 }135 Ok(())136}137138/// @title Contract, which allows users to operate with collections139#[solidity_interface(name = CollectionHelpers, events(CollectionHelpersEvents))]140impl<T> EvmCollectionHelpers<T>141where142 T: Config + pallet_common::Config + pallet_nonfungible::Config + pallet_refungible::Config,143{144 /// Create an NFT collection145 /// @param name Name of the collection146 /// @param description Informative description of the collection147 /// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications148 /// @return address Address of the newly created collection149 #[weight(<SelfWeightOf<T>>::create_collection())]150 #[solidity(rename_selector = "createNFTCollection")]151 fn create_nft_collection(152 &mut self,153 caller: caller,154 value: value,155 name: string,156 description: string,157 token_prefix: string,158 ) -> Result<address> {159 let (caller, name, description, token_prefix) =160 convert_data::<T>(caller, name, description, token_prefix)?;161 let data = CreateCollectionData {162 name,163 mode: CollectionMode::NFT,164 description,165 token_prefix,166 ..Default::default()167 };168 check_sent_amount_equals_collection_creation_price::<T>(value)?;169 let collection_helpers_address =170 T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());171 let collection_id = T::CollectionDispatch::create(172 caller,173 collection_helpers_address,174 data,175 Default::default(),176 )177 .map_err(dispatch_to_evm::<T>)?;178179 let address = pallet_common::eth::collection_id_to_address(collection_id);180 Ok(address)181 }182 /// Create an NFT collection183 /// @param name Name of the collection184 /// @param description Informative description of the collection185 /// @param tokenPrefix Token prefix to represent the collection tokens in UI and user applications186 /// @return address Address of the newly created collection187 #[weight(<SelfWeightOf<T>>::create_collection())]188 #[deprecated(note = "mathod was renamed to `create_nft_collection`, prefer it instead")]189 #[solidity(hide)]190 fn create_nonfungible_collection(191 &mut self,192 caller: caller,193 value: value,194 name: string,195 description: string,196 token_prefix: string,197 ) -> Result<address> {198 create_collection_internal::<T>(199 caller,200 value,201 name,202 CollectionMode::NFT,203 description,204 token_prefix,205 )206 }207208 #[weight(<SelfWeightOf<T>>::create_collection())]209 #[solidity(rename_selector = "createRFTCollection")]210 fn create_rft_collection(211 &mut self,212 caller: caller,213 value: value,214 name: string,215 description: string,216 token_prefix: string,217 ) -> Result<address> {218 create_collection_internal::<T>(219 caller,220 value,221 name,222 CollectionMode::ReFungible,223 description,224 token_prefix,225 )226 }227228 #[weight(<SelfWeightOf<T>>::create_collection())]229 #[solidity(rename_selector = "createFTCollection")]230 fn create_fungible_collection(231 &mut self,232 caller: caller,233 value: value,234 name: string,235 decimals: uint8,236 description: string,237 token_prefix: string,238 ) -> Result<address> {239 create_collection_internal::<T>(240 caller,241 value,242 name,243 CollectionMode::Fungible(decimals),244 description,245 token_prefix,246 )247 }248249 #[solidity(rename_selector = "makeCollectionERC721MetadataCompatible")]250 fn make_collection_metadata_compatible(251 &mut self,252 caller: caller,253 collection: address,254 base_uri: string,255 ) -> Result<()> {256 let caller = T::CrossAccountId::from_eth(caller);257 let collection =258 pallet_common::eth::map_eth_to_id(&collection).ok_or("not a collection address")?;259 let mut collection =260 <crate::CollectionHandle<T>>::new(collection).ok_or("collection not found")?;261262 if !matches!(263 collection.mode,264 CollectionMode::NFT | CollectionMode::ReFungible265 ) {266 return Err("target collection should be either NFT or Refungible".into());267 }268269 self.recorder().consume_sstore()?;270 collection271 .check_is_owner_or_admin(&caller)272 .map_err(dispatch_to_evm::<T>)?;273274 if collection.flags.erc721metadata {275 return Err("target collection is already Erc721Metadata compatible".into());276 }277 collection.flags.erc721metadata = true;278279 let all_permissions = <pallet_common::CollectionPropertyPermissions<T>>::get(collection.id);280 if all_permissions.get(&key::url()).is_none() {281 self.recorder().consume_sstore()?;282 <PalletCommon<T>>::set_property_permission(283 &collection,284 &caller,285 up_data_structs::PropertyKeyPermission {286 key: key::url(),287 permission: up_data_structs::PropertyPermission {288 mutable: true,289 collection_admin: true,290 token_owner: false,291 },292 },293 )294 .map_err(dispatch_to_evm::<T>)?;295 }296 if all_permissions.get(&key::suffix()).is_none() {297 self.recorder().consume_sstore()?;298 <PalletCommon<T>>::set_property_permission(299 &collection,300 &caller,301 up_data_structs::PropertyKeyPermission {302 key: key::suffix(),303 permission: up_data_structs::PropertyPermission {304 mutable: true,305 collection_admin: true,306 token_owner: false,307 },308 },309 )310 .map_err(dispatch_to_evm::<T>)?;311 }312313 let all_properties = <pallet_common::CollectionProperties<T>>::get(collection.id);314 if all_properties.get(&key::base_uri()).is_none() && !base_uri.is_empty() {315 self.recorder().consume_sstore()?;316 <PalletCommon<T>>::set_collection_properties(317 &collection,318 &caller,319 vec![up_data_structs::Property {320 key: key::base_uri(),321 value: base_uri322 .into_bytes()323 .try_into()324 .map_err(|_| "base uri is too large")?,325 }],326 )327 .map_err(dispatch_to_evm::<T>)?;328 }329330 self.recorder().consume_sstore()?;331 collection.save().map_err(dispatch_to_evm::<T>)?;332333 Ok(())334 }335336 #[weight(<SelfWeightOf<T>>::destroy_collection())]337 fn destroy_collection(&mut self, caller: caller, collection_address: address) -> Result<void> {338 let caller = T::CrossAccountId::from_eth(caller);339340 let collection_id = pallet_common::eth::map_eth_to_id(&collection_address)341 .ok_or("Invalid collection address format")?;342 <Pallet<T>>::destroy_collection_internal(caller, collection_id)343 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)344 }345346 /// Check if a collection exists347 /// @param collectionAddress Address of the collection in question348 /// @return bool Does the collection exist?349 fn is_collection_exist(&self, _caller: caller, collection_address: address) -> Result<bool> {350 if let Some(id) = pallet_common::eth::map_eth_to_id(&collection_address) {351 let collection_id = id;352 return Ok(<CollectionById<T>>::contains_key(collection_id));353 }354355 Ok(false)356 }357358 fn collection_creation_fee(&self) -> Result<value> {359 let price: u128 = T::CollectionCreationPrice::get()360 .try_into()361 .map_err(|_| ()) // workaround for `expect` requiring `Debug` trait362 .expect("Collection creation price should be convertible to u128");363 Ok(price.into())364 }365366 /// Returns address of a collection.367 /// @param collectionId - CollectionId of the collection368 /// @return eth mirror address of the collection369 fn collection_address(&self, collection_id: uint32) -> Result<address> {370 Ok(collection_id_to_address(collection_id.into()))371 }372373 /// Returns collectionId of a collection.374 /// @param collectionAddress - Eth address of the collection375 /// @return collectionId of the collection376 fn collection_id(&self, collection_address: address) -> Result<uint32> {377 map_eth_to_id(&collection_address)378 .map(|id| id.0)379 .ok_or(Error::Revert(format!(380 "failed to convert address {} into collectionId.",381 collection_address382 )))383 }384}385386/// Implements [`OnMethodCall`], which delegates call to [`EvmCollectionHelpers`]387pub struct CollectionHelpersOnMethodCall<T: Config>(PhantomData<*const T>);388impl<T: Config + pallet_nonfungible::Config + pallet_refungible::Config> OnMethodCall<T>389 for CollectionHelpersOnMethodCall<T>390{391 fn is_reserved(contract: &sp_core::H160) -> bool {392 contract == &T::ContractAddress::get()393 }394395 fn is_used(contract: &sp_core::H160) -> bool {396 contract == &T::ContractAddress::get()397 }398399 fn call(handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {400 if handle.code_address() != T::ContractAddress::get() {401 return None;402 }403404 let helpers =405 EvmCollectionHelpers::<T>(SubstrateRecorder::<T>::new(handle.remaining_gas()));406 pallet_evm_coder_substrate::call(handle, helpers)407 }408409 fn get_code(contract: &sp_core::H160) -> Option<Vec<u8>> {410 (contract == &T::ContractAddress::get())411 .then(|| include_bytes!("./stubs/CollectionHelpers.raw").to_vec())412 }413}414415generate_stubgen!(collection_helper_impl, CollectionHelpersCall<()>, true);416generate_stubgen!(collection_helper_iface, CollectionHelpersCall<()>, false);417418fn error_field_too_long(feild: &str, bound: usize) -> Error {419 Error::Revert(format!("{} is too long. Max length is {}.", feild, bound))420}pallets/unique/src/eth/stubs/CollectionHelpers.rawdiffbeforeafterbothbinary blob — no preview
pallets/unique/src/eth/stubs/CollectionHelpers.soldiffbeforeafterboth--- a/pallets/unique/src/eth/stubs/CollectionHelpers.sol
+++ b/pallets/unique/src/eth/stubs/CollectionHelpers.sol
@@ -24,7 +24,7 @@
}
/// @title Contract, which allows users to operate with collections
-/// @dev the ERC-165 identifier for this interface is 0x7dea03b1
+/// @dev the ERC-165 identifier for this interface is 0xe65011aa
contract CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents {
/// Create an NFT collection
/// @param name Name of the collection
@@ -130,4 +130,28 @@
dummy;
return 0;
}
+
+ /// Returns address of a collection.
+ /// @param collectionId - CollectionId of the collection
+ /// @return eth mirror address of the collection
+ /// @dev EVM selector for this function is: 0x2e716683,
+ /// or in textual repr: collectionAddress(uint32)
+ function collectionAddress(uint32 collectionId) public view returns (address) {
+ require(false, stub_error);
+ collectionId;
+ dummy;
+ return 0x0000000000000000000000000000000000000000;
+ }
+
+ /// Returns collectionId of a collection.
+ /// @param collectionAddress - Eth address of the collection
+ /// @return collectionId of the collection
+ /// @dev EVM selector for this function is: 0xb5cb7498,
+ /// or in textual repr: collectionId(address)
+ function collectionId(address collectionAddress) public view returns (uint32) {
+ require(false, stub_error);
+ collectionAddress;
+ dummy;
+ return 0;
+ }
}
tests/src/eth/abi/collectionHelpers.jsondiffbeforeafterboth--- a/tests/src/eth/abi/collectionHelpers.json
+++ b/tests/src/eth/abi/collectionHelpers.json
@@ -32,6 +32,15 @@
"type": "event"
},
{
+ "inputs": [
+ { "internalType": "uint32", "name": "collectionId", "type": "uint32" }
+ ],
+ "name": "collectionAddress",
+ "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
"inputs": [],
"name": "collectionCreationFee",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
@@ -40,6 +49,19 @@
},
{
"inputs": [
+ {
+ "internalType": "address",
+ "name": "collectionAddress",
+ "type": "address"
+ }
+ ],
+ "name": "collectionId",
+ "outputs": [{ "internalType": "uint32", "name": "", "type": "uint32" }],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
{ "internalType": "string", "name": "name", "type": "string" },
{ "internalType": "uint8", "name": "decimals", "type": "uint8" },
{ "internalType": "string", "name": "description", "type": "string" },
tests/src/eth/api/CollectionHelpers.soldiffbeforeafterboth--- a/tests/src/eth/api/CollectionHelpers.sol
+++ b/tests/src/eth/api/CollectionHelpers.sol
@@ -19,7 +19,7 @@
}
/// @title Contract, which allows users to operate with collections
-/// @dev the ERC-165 identifier for this interface is 0x7dea03b1
+/// @dev the ERC-165 identifier for this interface is 0xe65011aa
interface CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents {
/// Create an NFT collection
/// @param name Name of the collection
@@ -78,4 +78,18 @@
/// @dev EVM selector for this function is: 0xd23a7ab1,
/// or in textual repr: collectionCreationFee()
function collectionCreationFee() external view returns (uint256);
+
+ /// Returns address of a collection.
+ /// @param collectionId - CollectionId of the collection
+ /// @return eth mirror address of the collection
+ /// @dev EVM selector for this function is: 0x2e716683,
+ /// or in textual repr: collectionAddress(uint32)
+ function collectionAddress(uint32 collectionId) external view returns (address);
+
+ /// Returns collectionId of a collection.
+ /// @param collectionAddress - Eth address of the collection
+ /// @return collectionId of the collection
+ /// @dev EVM selector for this function is: 0xb5cb7498,
+ /// or in textual repr: collectionId(address)
+ function collectionId(address collectionAddress) external view returns (uint32);
}
tests/src/eth/collectionHelperAddress.test.tsdiffbeforeafterboth--- a/tests/src/eth/collectionHelperAddress.test.ts
+++ b/tests/src/eth/collectionHelperAddress.test.ts
@@ -16,10 +16,11 @@
import {itEth, usingEthPlaygrounds, expect} from './util';
import {IKeyringPair} from '@polkadot/types/types';
+import {Pallets} from '../util';
const EVM_COLLECTION_HELPERS_ADDRESS = '0x6c4e9fe1ae37a41e93cee429e8e1881abdcbb54f';
-describe('[eth]CollectionHelerpAddress test: ERC721 ', () => {
+describe('[eth]CollectionHelperAddress test: ERC20/ERC721 ', () => {
let donor: IKeyringPair;
before(async function() {
@@ -28,18 +29,22 @@
});
});
- itEth('NFT\\RFT', async ({helper}) => {
+ itEth('NFT', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
const {collectionAddress: nftCollectionAddress} = await helper.eth.createNFTCollection(owner, 'Sponsor', 'absolutely anything', 'ROC');
const nftCollection = helper.ethNativeContract.collection(nftCollectionAddress, 'nft', owner);
-
- const {collectionAddress: rftCollectionAddress} = await helper.eth.createRFTCollection(owner, 'Sponsor', 'absolutely anything', 'ROC');
- const rftCollection = helper.ethNativeContract.collection(rftCollectionAddress, 'rft', owner);
expect((await nftCollection.methods.collectionHelperAddress().call())
.toString().toLowerCase()).to.be.equal(EVM_COLLECTION_HELPERS_ADDRESS);
-
+ });
+
+ itEth.ifWithPallets('RFT ', [Pallets.ReFungible], async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+
+ const {collectionAddress: rftCollectionAddress} = await helper.eth.createRFTCollection(owner, 'Sponsor', 'absolutely anything', 'ROC');
+
+ const rftCollection = helper.ethNativeContract.collection(rftCollectionAddress, 'rft', owner);
expect((await rftCollection.methods.collectionHelperAddress().call())
.toString().toLowerCase()).to.be.equal(EVM_COLLECTION_HELPERS_ADDRESS);
});
@@ -53,5 +58,15 @@
expect((await collection.methods.collectionHelperAddress().call())
.toString().toLowerCase()).to.be.equal(EVM_COLLECTION_HELPERS_ADDRESS);
});
+
+ itEth('[collectionHelpers] convert collectionId into address', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const collectionId = 7;
+ const collectionAddress = helper.ethAddress.fromCollectionId(collectionId);
+ const helperContract = helper.ethNativeContract.collectionHelpers(owner);
+
+ expect(await helperContract.methods.collectionAddress(collectionId).call()).to.be.equal(collectionAddress);
+ expect(parseInt(await helperContract.methods.collectionId(collectionAddress).call())).to.be.equal(collectionId);
+ });
});
tests/src/interfaces/lookup.tsdiffbeforeafterboth--- a/tests/src/interfaces/lookup.ts
+++ b/tests/src/interfaces/lookup.ts
@@ -2936,13 +2936,13 @@
}
},
/**
- * Lookup338: pallet_maintenance::pallet::Call<T>
+ * Lookup340: pallet_maintenance::pallet::Call<T>
**/
PalletMaintenanceCall: {
_enum: ['enable', 'disable']
},
/**
- * Lookup339: pallet_test_utils::pallet::Call<T>
+ * Lookup341: pallet_test_utils::pallet::Call<T>
**/
PalletTestUtilsCall: {
_enum: {
tests/src/interfaces/types-lookup.tsdiffbeforeafterboth--- a/tests/src/interfaces/types-lookup.ts
+++ b/tests/src/interfaces/types-lookup.ts
@@ -3175,14 +3175,14 @@
readonly type: 'Begin' | 'SetData' | 'Finish' | 'InsertEthLogs' | 'InsertEvents';
}
- /** @name PalletMaintenanceCall (338) */
+ /** @name PalletMaintenanceCall (340) */
interface PalletMaintenanceCall extends Enum {
readonly isEnable: boolean;
readonly isDisable: boolean;
readonly type: 'Enable' | 'Disable';
}
- /** @name PalletTestUtilsCall (339) */
+ /** @name PalletTestUtilsCall (341) */
interface PalletTestUtilsCall extends Enum {
readonly isEnable: boolean;
readonly isSetTestValue: boolean;