12345678910111213141516171819use core::marker::PhantomData;20use evm_coder::{execution::*, generate_stubgen, solidity_interface, solidity, weight, types::*};21use ethereum as _;22use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};23use pallet_evm::{OnMethodCall, PrecompileResult, account::CrossAccountId, PrecompileHandle};24use up_data_structs::{25 CollectionName, CollectionDescription, CollectionTokenPrefix, CreateCollectionData,26 CollectionMode, PropertyValue,27};28use frame_support::traits::Get;29use pallet_common::{30 CollectionById,31 erc::{32 static_property::{key, value as property_value},33 CollectionHelpersEvents,34 },35 dispatch::CollectionDispatch,36};37use crate::{SelfWeightOf, Config, weights::WeightInfo};3839use sp_std::vec::Vec;40use alloc::format;414243pub struct EvmCollectionHelpers<T: Config>(SubstrateRecorder<T>);44impl<T: Config> WithRecorder<T> for EvmCollectionHelpers<T> {45 fn recorder(&self) -> &SubstrateRecorder<T> {46 &self.047 }4849 fn into_recorder(self) -> SubstrateRecorder<T> {50 self.051 }52}5354fn convert_data<T: Config>(55 caller: caller,56 name: string,57 description: string,58 token_prefix: string,59 base_uri: string,60) -> Result<(61 T::CrossAccountId,62 CollectionName,63 CollectionDescription,64 CollectionTokenPrefix,65 PropertyValue,66)> {67 let caller = T::CrossAccountId::from_eth(caller);68 let name = name69 .encode_utf16()70 .collect::<Vec<u16>>()71 .try_into()72 .map_err(|_| error_field_too_long(stringify!(name), CollectionName::bound()))?;73 let description = description74 .encode_utf16()75 .collect::<Vec<u16>>()76 .try_into()77 .map_err(|_| {78 error_field_too_long(stringify!(description), CollectionDescription::bound())79 })?;80 let token_prefix = token_prefix.into_bytes().try_into().map_err(|_| {81 error_field_too_long(stringify!(token_prefix), CollectionTokenPrefix::bound())82 })?;83 let base_uri_value = base_uri84 .into_bytes()85 .try_into()86 .map_err(|_| error_field_too_long(stringify!(token_prefix), PropertyValue::bound()))?;87 Ok((caller, name, description, token_prefix, base_uri_value))88}8990fn make_data<T: Config>(91 name: CollectionName,92 mode: CollectionMode,93 description: CollectionDescription,94 token_prefix: CollectionTokenPrefix,95 base_uri_value: PropertyValue,96 add_properties: bool,97) -> Result<CreateCollectionData<T::AccountId>> {98 let mut properties = up_data_structs::CollectionPropertiesVec::default();99 let mut token_property_permissions =100 up_data_structs::CollectionPropertiesPermissionsVec::default();101102 token_property_permissions103 .try_push(up_data_structs::PropertyKeyPermission {104 key: key::url(),105 permission: up_data_structs::PropertyPermission {106 mutable: false,107 collection_admin: true,108 token_owner: false,109 },110 })111 .map_err(|e| Error::Revert(format!("{:?}", e)))?;112113 if add_properties {114 token_property_permissions115 .try_push(up_data_structs::PropertyKeyPermission {116 key: key::suffix(),117 permission: up_data_structs::PropertyPermission {118 mutable: false,119 collection_admin: true,120 token_owner: false,121 },122 })123 .map_err(|e| Error::Revert(format!("{:?}", e)))?;124125 properties126 .try_push(up_data_structs::Property {127 key: key::schema_name(),128 value: property_value::erc721(),129 })130 .map_err(|e| Error::Revert(format!("{:?}", e)))?;131132 if !base_uri_value.is_empty() {133 properties134 .try_push(up_data_structs::Property {135 key: key::base_uri(),136 value: base_uri_value,137 })138 .map_err(|e| Error::Revert(format!("{:?}", e)))?;139 }140 }141142 let data = CreateCollectionData {143 name,144 mode,145 description,146 token_prefix,147 token_property_permissions,148 properties,149 ..Default::default()150 };151 Ok(data)152}153154155#[solidity_interface(name = "CollectionHelpers", events(CollectionHelpersEvents))]156impl<T> EvmCollectionHelpers<T>157where158 T: Config + pallet_nonfungible::Config + pallet_refungible::Config,159{160 161 162 163 164 165 #[weight(<SelfWeightOf<T>>::create_collection())]166 fn create_nonfungible_collection(167 &mut self,168 caller: caller,169 name: string,170 description: string,171 token_prefix: string,172 ) -> Result<address> {173 let (caller, name, description, token_prefix, _base_uri_value) =174 convert_data::<T>(caller, name, description, token_prefix, "".into())?;175 let data = make_data::<T>(176 name,177 CollectionMode::NFT,178 description,179 token_prefix,180 Default::default(),181 false,182 )?;183 let collection_id = T::CollectionDispatch::create(caller, data)184 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;185186 let address = pallet_common::eth::collection_id_to_address(collection_id);187 Ok(address)188 }189190 #[weight(<SelfWeightOf<T>>::create_collection())]191 #[solidity(rename_selector = "createERC721MetadataCompatibleCollection")]192 fn create_nonfungible_collection_with_properties(193 &mut self,194 caller: caller,195 name: string,196 description: string,197 token_prefix: string,198 base_uri: string,199 ) -> Result<address> {200 let (caller, name, description, token_prefix, base_uri_value) =201 convert_data::<T>(caller, name, description, token_prefix, base_uri)?;202 let data = make_data::<T>(203 name,204 CollectionMode::NFT,205 description,206 token_prefix,207 base_uri_value,208 true,209 )?;210 let collection_id = T::CollectionDispatch::create(caller, data)211 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;212213 let address = pallet_common::eth::collection_id_to_address(collection_id);214 Ok(address)215 }216217 #[weight(<SelfWeightOf<T>>::create_collection())]218 fn create_refungible_collection(219 &self,220 caller: caller,221 name: string,222 description: string,223 token_prefix: string,224 ) -> Result<address> {225 let (caller, name, description, token_prefix, _base_uri) =226 convert_data::<T>(caller, name, description, token_prefix, "".into())?;227 let data = make_data::<T>(228 name,229 CollectionMode::ReFungible,230 description,231 token_prefix,232 Default::default(),233 false,234 )?;235 let collection_id = T::CollectionDispatch::create(caller, data)236 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;237238 let address = pallet_common::eth::collection_id_to_address(collection_id);239 Ok(address)240 }241242 #[weight(<SelfWeightOf<T>>::create_collection())]243 #[solidity(rename_selector = "createERC721MetadataCompatibleRFTCollection")]244 fn create_refungible_collection_with_properties(245 &mut self,246 caller: caller,247 name: string,248 description: string,249 token_prefix: string,250 base_uri: string,251 ) -> Result<address> {252 let (caller, name, description, token_prefix, base_uri_value) =253 convert_data::<T>(caller, name, description, token_prefix, base_uri)?;254 let data = make_data::<T>(255 name,256 CollectionMode::NFT,257 description,258 token_prefix,259 base_uri_value,260 true,261 )?;262 let collection_id = T::CollectionDispatch::create(caller, data)263 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;264265 let address = pallet_common::eth::collection_id_to_address(collection_id);266 Ok(address)267 }268269 270 271 272 fn is_collection_exist(&self, _caller: caller, collection_address: address) -> Result<bool> {273 if let Some(id) = pallet_common::eth::map_eth_to_id(&collection_address) {274 let collection_id = id;275 return Ok(<CollectionById<T>>::contains_key(collection_id));276 }277278 Ok(false)279 }280}281282283pub struct CollectionHelpersOnMethodCall<T: Config>(PhantomData<*const T>);284impl<T: Config + pallet_nonfungible::Config + pallet_refungible::Config> OnMethodCall<T>285 for CollectionHelpersOnMethodCall<T>286{287 fn is_reserved(contract: &sp_core::H160) -> bool {288 contract == &T::ContractAddress::get()289 }290291 fn is_used(contract: &sp_core::H160) -> bool {292 contract == &T::ContractAddress::get()293 }294295 fn call(handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {296 if handle.code_address() != T::ContractAddress::get() {297 return None;298 }299300 let helpers =301 EvmCollectionHelpers::<T>(SubstrateRecorder::<T>::new(handle.remaining_gas()));302 pallet_evm_coder_substrate::call(handle, helpers)303 }304305 fn get_code(contract: &sp_core::H160) -> Option<Vec<u8>> {306 (contract == &T::ContractAddress::get())307 .then(|| include_bytes!("./stubs/CollectionHelpers.raw").to_vec())308 }309}310311generate_stubgen!(collection_helper_impl, CollectionHelpersCall<()>, true);312generate_stubgen!(collection_helper_iface, CollectionHelpersCall<()>, false);313314fn error_field_too_long(feild: &str, bound: usize) -> Error {315 Error::Revert(format!("{} is too long. Max length is {}.", feild, bound))316}