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};36use crate::{SelfWeightOf, Config, weights::WeightInfo};3738use sp_std::vec::Vec;39use alloc::format;404142pub struct EvmCollectionHelpers<T: Config>(SubstrateRecorder<T>);43impl<T: Config> WithRecorder<T> for EvmCollectionHelpers<T> {44 fn recorder(&self) -> &SubstrateRecorder<T> {45 &self.046 }4748 fn into_recorder(self) -> SubstrateRecorder<T> {49 self.050 }51}5253fn convert_data<T: Config>(54 caller: caller,55 name: string,56 description: string,57 token_prefix: string,58 base_uri: string,59) -> Result<(60 T::CrossAccountId,61 CollectionName,62 CollectionDescription,63 CollectionTokenPrefix,64 PropertyValue,65)> {66 let caller = T::CrossAccountId::from_eth(caller);67 let name = name68 .encode_utf16()69 .collect::<Vec<u16>>()70 .try_into()71 .map_err(|_| error_feild_too_long(stringify!(name), CollectionName::bound()))?;72 let description = description73 .encode_utf16()74 .collect::<Vec<u16>>()75 .try_into()76 .map_err(|_| {77 error_feild_too_long(stringify!(description), CollectionDescription::bound())78 })?;79 let token_prefix = token_prefix.into_bytes().try_into().map_err(|_| {80 error_feild_too_long(stringify!(token_prefix), CollectionTokenPrefix::bound())81 })?;82 let base_uri_value = base_uri83 .into_bytes()84 .try_into()85 .map_err(|_| error_feild_too_long(stringify!(token_prefix), PropertyValue::bound()))?;86 Ok((caller, name, description, token_prefix, base_uri_value))87}8889fn make_data<T: Config>(90 name: CollectionName,91 mode: CollectionMode,92 description: CollectionDescription,93 token_prefix: CollectionTokenPrefix,94 base_uri_value: PropertyValue,95 add_properties: bool,96) -> Result<CreateCollectionData<T::AccountId>> {97 let mut properties = up_data_structs::CollectionPropertiesVec::default();98 let mut token_property_permissions =99 up_data_structs::CollectionPropertiesPermissionsVec::default();100101 token_property_permissions102 .try_push(up_data_structs::PropertyKeyPermission {103 key: key::url(),104 permission: up_data_structs::PropertyPermission {105 mutable: false,106 collection_admin: true,107 token_owner: false,108 },109 })110 .map_err(|e| Error::Revert(format!("{:?}", e)))?;111112 if add_properties {113 token_property_permissions114 .try_push(up_data_structs::PropertyKeyPermission {115 key: key::suffix(),116 permission: up_data_structs::PropertyPermission {117 mutable: false,118 collection_admin: true,119 token_owner: false,120 },121 })122 .map_err(|e| Error::Revert(format!("{:?}", e)))?;123124 properties125 .try_push(up_data_structs::Property {126 key: key::schema_name(),127 value: property_value::erc721(),128 })129 .map_err(|e| Error::Revert(format!("{:?}", e)))?;130131 if !base_uri_value.is_empty() {132 properties133 .try_push(up_data_structs::Property {134 key: key::base_uri(),135 value: base_uri_value,136 })137 .map_err(|e| Error::Revert(format!("{:?}", e)))?;138 }139 }140141 let data = CreateCollectionData {142 name,143 mode,144 description,145 token_prefix,146 token_property_permissions,147 properties,148 ..Default::default()149 };150 Ok(data)151}152153154#[solidity_interface(name = "CollectionHelpers", events(CollectionHelpersEvents))]155impl<T> EvmCollectionHelpers<T>156where157 T: Config + pallet_nonfungible::Config + pallet_refungible::Config,158{159 160 161 162 163 164 #[weight(<SelfWeightOf<T>>::create_collection())]165 fn create_nonfungible_collection(166 &mut self,167 caller: caller,168 name: string,169 description: string,170 token_prefix: string,171 ) -> Result<address> {172 let (caller, name, description, token_prefix, _base_uri_value) =173 convert_data::<T>(caller, name, description, token_prefix, "".into())?;174 let data = make_data::<T>(175 name,176 CollectionMode::NFT,177 description,178 token_prefix,179 Default::default(),180 false,181 )?;182 let collection_id =183 <pallet_nonfungible::Pallet<T>>::init_collection(caller.clone(), data, false)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 =211 <pallet_nonfungible::Pallet<T>>::init_collection(caller.clone(), data, true)212 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;213214 let address = pallet_common::eth::collection_id_to_address(collection_id);215 Ok(address)216 }217218 #[weight(<SelfWeightOf<T>>::create_collection())]219 fn create_refungible_collection(220 &self,221 caller: caller,222 name: string,223 description: string,224 token_prefix: string,225 ) -> Result<address> {226 let (caller, name, description, token_prefix, _base_uri) =227 convert_data::<T>(caller, name, description, token_prefix, "".into())?;228 let data = make_data::<T>(229 name,230 CollectionMode::ReFungible,231 description,232 token_prefix,233 Default::default(),234 false,235 )?;236 let collection_id = <pallet_refungible::Pallet<T>>::init_collection(caller.clone(), data)237 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;238239 let address = pallet_common::eth::collection_id_to_address(collection_id);240 Ok(address)241 }242243 #[weight(<SelfWeightOf<T>>::create_collection())]244 #[solidity(rename_selector = "createERC721MetadataCompatibleRFTCollection")]245 fn create_refungible_collection_with_properties(246 &mut self,247 caller: caller,248 name: string,249 description: string,250 token_prefix: string,251 base_uri: string,252 ) -> Result<address> {253 let (caller, name, description, token_prefix, base_uri_value) =254 convert_data::<T>(caller, name, description, token_prefix, base_uri)?;255 let data = make_data::<T>(256 name,257 CollectionMode::NFT,258 description,259 token_prefix,260 base_uri_value,261 true,262 )?;263 let collection_id = <pallet_refungible::Pallet<T>>::init_collection(caller.clone(), data)264 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;265266 let address = pallet_common::eth::collection_id_to_address(collection_id);267 Ok(address)268 }269270 271 272 273 fn is_collection_exist(&self, _caller: caller, collection_address: address) -> Result<bool> {274 if let Some(id) = pallet_common::eth::map_eth_to_id(&collection_address) {275 let collection_id = id;276 return Ok(<CollectionById<T>>::contains_key(collection_id));277 }278279 Ok(false)280 }281}282283284pub struct CollectionHelpersOnMethodCall<T: Config>(PhantomData<*const T>);285impl<T: Config + pallet_nonfungible::Config + pallet_refungible::Config> OnMethodCall<T>286 for CollectionHelpersOnMethodCall<T>287{288 fn is_reserved(contract: &sp_core::H160) -> bool {289 contract == &T::ContractAddress::get()290 }291292 fn is_used(contract: &sp_core::H160) -> bool {293 contract == &T::ContractAddress::get()294 }295296 fn call(handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {297 if handle.code_address() != T::ContractAddress::get() {298 return None;299 }300301 let helpers =302 EvmCollectionHelpers::<T>(SubstrateRecorder::<T>::new(handle.remaining_gas()));303 pallet_evm_coder_substrate::call(handle, helpers)304 }305306 fn get_code(contract: &sp_core::H160) -> Option<Vec<u8>> {307 (contract == &T::ContractAddress::get())308 .then(|| include_bytes!("./stubs/CollectionHelpers.raw").to_vec())309 }310}311312generate_stubgen!(collection_helper_impl, CollectionHelpersCall<()>, true);313generate_stubgen!(collection_helper_iface, CollectionHelpersCall<()>, false);314315fn error_feild_too_long(feild: &str, bound: usize) -> Error {316 Error::Revert(format!("{} is too long. Max length is {}.", feild, bound))317}