1234567891011121314151617pub mod sponsoring;1819use fp_evm::PrecompileResult;20use pallet_common::{21 CollectionById,22 erc::CommonEvmHandler,23 eth::{map_eth_to_id, map_eth_to_token_id},24};25use pallet_fungible::FungibleHandle;26use pallet_nonfungible::NonfungibleHandle;27use pallet_refungible::{RefungibleHandle, erc::RefungibleTokenHandle};28use sp_std::borrow::ToOwned;29use sp_std::vec::Vec;30use sp_core::{H160, U256};31use crate::{CollectionMode, Config, dispatch::Dispatched};32use pallet_common::CollectionHandle;3334pub struct UniqueErcSupport<T: Config>(core::marker::PhantomData<T>);3536impl<T: Config> pallet_evm::OnMethodCall<T> for UniqueErcSupport<T> {37 fn is_reserved(target: &H160) -> bool {38 map_eth_to_id(target).is_some()39 }40 fn is_used(target: &H160) -> bool {41 map_eth_to_id(target)42 .map(<CollectionById<T>>::contains_key)43 .unwrap_or(false)44 }45 fn get_code(target: &H160) -> Option<Vec<u8>> {46 if let Some(collection_id) = map_eth_to_id(target) {47 let collection = <CollectionById<T>>::get(collection_id)?;48 Some(49 match collection.mode {50 CollectionMode::NFT => <NonfungibleHandle<T>>::CODE,51 CollectionMode::Fungible(_) => <FungibleHandle<T>>::CODE,52 CollectionMode::ReFungible => <RefungibleHandle<T>>::CODE,53 }54 .to_owned(),55 )56 } else if let Some((collection_id, _token_id)) = map_eth_to_token_id(target) {57 let collection = <CollectionById<T>>::get(collection_id)?;58 if collection.mode != CollectionMode::ReFungible {59 return None;60 }61 62 Some(<RefungibleTokenHandle<T>>::CODE.to_owned())63 } else {64 None65 }66 }67 fn call(68 source: &H160,69 target: &H160,70 gas_limit: u64,71 input: &[u8],72 value: U256,73 ) -> Option<PrecompileResult> {74 if let Some(collection_id) = map_eth_to_id(target) {75 let collection = <CollectionHandle<T>>::new_with_gas_limit(collection_id, gas_limit)?;76 let dispatched = Dispatched::dispatch(collection);7778 match dispatched {79 Dispatched::Fungible(h) => h.call(source, input, value),80 Dispatched::Nonfungible(h) => h.call(source, input, value),81 Dispatched::Refungible(h) => h.call(source, input, value),82 }83 } else if let Some((collection_id, token_id)) = map_eth_to_token_id(target) {84 let collection = <CollectionHandle<T>>::new_with_gas_limit(collection_id, gas_limit)?;85 if collection.mode != CollectionMode::ReFungible {86 return None;87 }8889 let handle = RefungibleHandle::cast(collection);90 91 RefungibleTokenHandle(handle, token_id).call(source, input, value)92 } else {93 None94 }95 }96}9798pub mod evm_collection {99 use core::marker::PhantomData;100 use evm_coder::{execution::*, generate_stubgen, solidity_interface, types::*, ToLog};101 use ethereum as _;102 use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};103 use pallet_evm::{OnMethodCall, PrecompileResult, account::CrossAccountId};104 use up_data_structs::{105 CreateCollectionData, MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH,106 MAX_COLLECTION_NAME_LENGTH,107 };108 use frame_support::traits::Get;109 use sp_core::H160;110 use pallet_common::{CollectionHandle, save_eth};111 112 use sp_std::{vec::Vec, rc::Rc};113 use alloc::format;114 115 116 pub trait Config:117 frame_system::Config118 + pallet_evm_coder_substrate::Config119 + pallet_evm::account::Config120 + pallet_nonfungible::Config121 {122 type ContractAddress: Get<H160>;123 }124125 struct EvmCollection<T: Config>(Rc<SubstrateRecorder<T>>);126 impl<T: Config> WithRecorder<T> for EvmCollection<T> {127 fn recorder(&self) -> &SubstrateRecorder<T> {128 &self.0129 }130 131 fn into_recorder(self) -> Rc<SubstrateRecorder<T>> {132 self.0133 }134 }135 136 #[derive(ToLog)]137 pub enum EthCollectionEvent {138 CollectionCreated {139 #[indexed]140 owner: address,141 #[indexed]142 collection_id: address,143 },144 }145 146 #[solidity_interface(name = "Collection")]147 impl<T: Config> EvmCollection<T> {148 fn create_721_collection(149 &self,150 caller: caller,151 name: string,152 description: string,153 token_prefix: string,154 ) -> Result<address> {155 let caller = T::CrossAccountId::from_eth(caller);156 let name = name157 .encode_utf16()158 .collect::<Vec<u16>>()159 .try_into()160 .map_err(|_| error_feild_too_long(stringify!(name), MAX_COLLECTION_NAME_LENGTH))?;161 let description = description162 .encode_utf16()163 .collect::<Vec<u16>>()164 .try_into()165 .map_err(|_| {166 error_feild_too_long(stringify!(description), MAX_COLLECTION_DESCRIPTION_LENGTH)167 })?;168 let token_prefix = token_prefix169 .into_bytes()170 .try_into()171 .map_err(|_| error_feild_too_long(stringify!(token_prefix), MAX_TOKEN_PREFIX_LENGTH))?;172 173 let data = CreateCollectionData {174 name,175 description,176 token_prefix,177 ..Default::default()178 };179 180 let collection_id =181 <pallet_nonfungible::Pallet<T>>::init_collection(caller.as_sub().clone(), data)182 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;183 184 let address = pallet_common::eth::collection_id_to_address(collection_id);185 self.0.log_mirrored(EthCollectionEvent::CollectionCreated {186 owner: *caller.as_eth(),187 collection_id: address,188 });189 Ok(address)190 }191 192 fn set_sponsor(193 &self,194 caller: caller,195 collection_address: address,196 sponsor: address,197 ) -> Result<void> {198 let mut collection = collection_from_address(collection_address, &self.0)?;199 check_is_owner(caller, &collection)?;200 201 let sponsor = T::CrossAccountId::from_eth(sponsor);202 collection.set_sponsor(sponsor.as_sub().clone());203 save_eth(collection)204 }205 206 fn confirm_sponsorship(&self, caller: caller, collection_address: address) -> Result<void> {207 let mut collection = collection_from_address(collection_address, &self.0)?;208 let caller = T::CrossAccountId::from_eth(caller);209 if !collection.confirm_sponsorship(caller.as_sub()) {210 return Err(Error::Revert("Caller is not set as sponsor".into()));211 }212 save_eth(collection)213 }214 215 fn set_limits(216 &self,217 caller: caller,218 collection_address: address,219 limits_json: string,220 ) -> Result<void> {221 let mut collection = collection_from_address(collection_address, &self.0)?;222 check_is_owner(caller, &collection)?;223 224 let limits = serde_json_core::from_str(limits_json.as_ref())225 .map_err(|e| Error::Revert(format!("Parse JSON error: {}", e)))?;226 collection.limits = limits.0;227 save_eth(collection)228 }229 }230 231 fn error_feild_too_long(feild: &str, bound: u32) -> Error {232 Error::Revert(format!("{} is too long. Max length is {}.", feild, bound))233 }234 235 fn collection_from_address<T: Config>(236 collection_address: address,237 recorder: &Rc<SubstrateRecorder<T>>,238 ) -> Result<CollectionHandle<T>> {239 let collection_id = pallet_common::eth::map_eth_to_id(&collection_address)240 .ok_or(Error::Revert("Contract is not an unique collection".into()))?;241 let collection =242 pallet_common::CollectionHandle::new_with_recorder(collection_id, recorder.clone())243 .ok_or(Error::Revert("Create collection handle error".into()))?;244 Ok(collection)245 }246 247 fn check_is_owner<T: Config>(caller: caller, collection: &CollectionHandle<T>) -> Result<()> {248 let caller = T::CrossAccountId::from_eth(caller);249 collection250 .check_is_owner(&caller)251 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;252 Ok(())253 }254 255 pub struct CollectionOnMethodCall<T: Config>(PhantomData<*const T>);256 impl<T: Config> OnMethodCall<T> for CollectionOnMethodCall<T> {257 fn is_reserved(contract: &sp_core::H160) -> bool {258 contract == &T::ContractAddress::get()259 }260 261 fn is_used(contract: &sp_core::H160) -> bool {262 contract == &T::ContractAddress::get()263 }264 265 fn call(266 source: &sp_core::H160,267 target: &sp_core::H160,268 gas_left: u64,269 input: &[u8],270 value: sp_core::U256,271 ) -> Option<PrecompileResult> {272 if target != &T::ContractAddress::get() {273 return None;274 }275 276 let helpers = EvmCollection::<T>(Rc::new(SubstrateRecorder::<T>::new(*target, gas_left)));277 pallet_evm_coder_substrate::call(*source, helpers, value, input)278 }279 280 fn get_code(contract: &sp_core::H160) -> Option<Vec<u8>> {281 (contract == &T::ContractAddress::get())282 .then(|| include_bytes!("./stubs/Collection.raw").to_vec())283 }284 }285 286 generate_stubgen!(collection_impl, CollectionCall<()>, true);287 generate_stubgen!(collection_iface, CollectionCall<()>, false);288}