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 pub trait Config:116 frame_system::Config117 + pallet_evm_coder_substrate::Config118 + pallet_evm::account::Config119 + pallet_nonfungible::Config120 {121 type ContractAddress: Get<H160>;122 }123124 struct EvmCollectionHelper<T: Config>(Rc<SubstrateRecorder<T>>);125 impl<T: Config> WithRecorder<T> for EvmCollectionHelper<T> {126 fn recorder(&self) -> &SubstrateRecorder<T> {127 &self.0128 }129 130 fn into_recorder(self) -> Rc<SubstrateRecorder<T>> {131 self.0132 }133 }134135 #[solidity_interface(name = "CollectionHelper")]136 impl<T: Config> EvmCollectionHelper<T> {137 fn create_721_collection(138 &self,139 caller: caller,140 name: string,141 description: string,142 token_prefix: string,143 ) -> Result<address> {144 let caller = T::CrossAccountId::from_eth(caller);145 let name = name146 .encode_utf16()147 .collect::<Vec<u16>>()148 .try_into()149 .map_err(|_| error_feild_too_long(stringify!(name), MAX_COLLECTION_NAME_LENGTH))?;150 let description = description151 .encode_utf16()152 .collect::<Vec<u16>>()153 .try_into()154 .map_err(|_| {155 error_feild_too_long(stringify!(description), MAX_COLLECTION_DESCRIPTION_LENGTH)156 })?;157 let token_prefix = token_prefix158 .into_bytes()159 .try_into()160 .map_err(|_| error_feild_too_long(stringify!(token_prefix), MAX_TOKEN_PREFIX_LENGTH))?;161 162 let data = CreateCollectionData {163 name,164 description,165 token_prefix,166 ..Default::default()167 };168 169 let collection_id =170 <pallet_nonfungible::Pallet<T>>::init_collection(caller.as_sub().clone(), data)171 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;172 173 let address = pallet_common::eth::collection_id_to_address(collection_id);174 self.0.log_mirrored(EthCollectionEvent::CollectionCreated {175 owner: *caller.as_eth(),176 collection_id: address,177 });178 Ok(address)179 }180 }181 182 struct EvmCollection<T: Config>(Rc<SubstrateRecorder<T>>);183 impl<T: Config> WithRecorder<T> for EvmCollection<T> {184 fn recorder(&self) -> &SubstrateRecorder<T> {185 &self.0186 }187 188 fn into_recorder(self) -> Rc<SubstrateRecorder<T>> {189 self.0190 }191 }192 193 #[derive(ToLog)]194 pub enum EthCollectionEvent {195 CollectionCreated {196 #[indexed]197 owner: address,198 #[indexed]199 collection_id: address,200 },201 }202 203 #[solidity_interface(name = "Collection")]204 impl<T: Config> EvmCollection<T> {205 fn set_sponsor(206 &self,207 caller: caller,208 sponsor: address,209 ) -> Result<void> {210 let mut collection = collection_from_address(self.contract_address(caller).unwrap(), &self.0)?;211 check_is_owner(caller, &collection)?;212 213 let sponsor = T::CrossAccountId::from_eth(sponsor);214 collection.set_sponsor(sponsor.as_sub().clone());215 save_eth(collection)216 }217 218 fn confirm_sponsorship(&self, caller: caller) -> Result<void> {219 let mut collection = collection_from_address(self.contract_address(caller).unwrap(), &self.0)?;220 let caller = T::CrossAccountId::from_eth(caller);221 if !collection.confirm_sponsorship(caller.as_sub()) {222 return Err(Error::Revert("Caller is not set as sponsor".into()));223 }224 save_eth(collection)225 }226 227 fn set_limits(228 &self,229 caller: caller,230 limits_json: string,231 ) -> Result<void> {232 let mut collection = collection_from_address(self.contract_address(caller).unwrap(), &self.0)?;233 check_is_owner(caller, &collection)?;234 235 let limits = serde_json_core::from_str(limits_json.as_ref())236 .map_err(|e| Error::Revert(format!("Parse JSON error: {}", e)))?;237 collection.limits = limits.0;238 save_eth(collection)239 }240241 fn contract_address(&self, _caller: caller) -> Result<address> {242 Ok(self.0.contract())243 }244 }245 246 fn error_feild_too_long(feild: &str, bound: u32) -> Error {247 Error::Revert(format!("{} is too long. Max length is {}.", feild, bound))248 }249 250 fn collection_from_address<T: Config>(251 collection_address: address,252 recorder: &Rc<SubstrateRecorder<T>>,253 ) -> Result<CollectionHandle<T>> {254 let collection_id = pallet_common::eth::map_eth_to_id(&collection_address)255 .ok_or(Error::Revert("Contract is not an unique collection".into()))?;256 let collection =257 pallet_common::CollectionHandle::new_with_recorder(collection_id, recorder.clone())258 .ok_or(Error::Revert("Create collection handle error".into()))?;259 Ok(collection)260 }261 262 fn check_is_owner<T: Config>(caller: caller, collection: &CollectionHandle<T>) -> Result<()> {263 let caller = T::CrossAccountId::from_eth(caller);264 collection265 .check_is_owner(&caller)266 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;267 Ok(())268 }269270 pub struct CollectionHelperOnMethodCall<T: Config>(PhantomData<*const T>);271 impl<T: Config> OnMethodCall<T> for CollectionHelperOnMethodCall<T> {272 fn is_reserved(contract: &sp_core::H160) -> bool {273 contract == &T::ContractAddress::get()274 }275 276 fn is_used(contract: &sp_core::H160) -> bool {277 contract == &T::ContractAddress::get()278 }279 280 fn call(281 source: &sp_core::H160,282 target: &sp_core::H160,283 gas_left: u64,284 input: &[u8],285 value: sp_core::U256,286 ) -> Option<PrecompileResult> {287 if target != &T::ContractAddress::get() {288 return None;289 }290 291 let helpers = EvmCollectionHelper::<T>(Rc::new(SubstrateRecorder::<T>::new(*target, gas_left)));292 pallet_evm_coder_substrate::call(*source, helpers, value, input)293 }294 295 fn get_code(contract: &sp_core::H160) -> Option<Vec<u8>> {296 (contract == &T::ContractAddress::get())297 .then(|| include_bytes!("./stubs/CollectionHelper.raw").to_vec())298 }299 }300 301 generate_stubgen!(collection_helper_impl, CollectionHelperCall<()>, true);302 generate_stubgen!(collection_helper_iface, CollectionHelperCall<()>, false);303 304 pub struct CollectionOnMethodCall<T: Config>(PhantomData<*const T>);305 impl<T: Config> OnMethodCall<T> for CollectionOnMethodCall<T> {306 fn is_reserved(contract: &sp_core::H160) -> bool {307 contract == &T::ContractAddress::get()308 }309 310 fn is_used(contract: &sp_core::H160) -> bool {311 contract == &T::ContractAddress::get()312 }313 314 fn call(315 source: &sp_core::H160,316 target: &sp_core::H160,317 gas_left: u64,318 input: &[u8],319 value: sp_core::U256,320 ) -> Option<PrecompileResult> {321 let helpers = EvmCollection::<T>(Rc::new(SubstrateRecorder::<T>::new(*target, gas_left)));322 pallet_evm_coder_substrate::call(*source, helpers, value, input)323 }324 325 fn get_code(contract: &sp_core::H160) -> Option<Vec<u8>> {326 (contract == &T::ContractAddress::get())327 .then(|| include_bytes!("./stubs/Collection.raw").to_vec())328 }329 }330 331 generate_stubgen!(collection_impl, CollectionCall<()>, true);332 generate_stubgen!(collection_iface, CollectionCall<()>, false);333}