1234567891011121314151617use evm_coder::{18 solidity_interface, solidity, ToLog,19 types::*,20 execution::{Result, Error},21};22pub use pallet_evm::{PrecompileOutput, PrecompileResult, account::CrossAccountId};23use pallet_evm_coder_substrate::dispatch_to_evm;24use sp_core::{H160, U256};25use sp_std::vec::Vec;26use up_data_structs::{Property, SponsoringRateLimit};27use alloc::format;2829use crate::{Pallet, CollectionHandle, Config, CollectionProperties};3031#[derive(ToLog)]32pub enum CollectionHelpersEvents {33 CollectionCreated {34 #[indexed]35 owner: address,36 #[indexed]37 collection_id: address,38 },39}40414243pub trait CommonEvmHandler {44 const CODE: &'static [u8];4546 fn call(self, source: &H160, input: &[u8], value: U256) -> Option<PrecompileResult>;47}4849#[solidity_interface(name = "Collection")]50impl<T: Config> CollectionHandle<T> {51 fn set_collection_property(&mut self, caller: caller, key: string, value: bytes) -> Result<()> {52 let caller = T::CrossAccountId::from_eth(caller);53 let key = <Vec<u8>>::from(key)54 .try_into()55 .map_err(|_| "key too large")?;56 let value = value.try_into().map_err(|_| "value too large")?;5758 <Pallet<T>>::set_collection_property(self, &caller, Property { key, value })59 .map_err(dispatch_to_evm::<T>)60 }6162 fn delete_collection_property(&mut self, caller: caller, key: string) -> Result<()> {63 let caller = T::CrossAccountId::from_eth(caller);64 let key = <Vec<u8>>::from(key)65 .try_into()66 .map_err(|_| "key too large")?;6768 <Pallet<T>>::delete_collection_property(self, &caller, key).map_err(dispatch_to_evm::<T>)69 }7071 72 fn collection_property(&self, key: string) -> Result<bytes> {73 let key = <Vec<u8>>::from(key)74 .try_into()75 .map_err(|_| "key too large")?;7677 let props = <CollectionProperties<T>>::get(self.id);78 let prop = props.get(&key).ok_or("key not found")?;7980 Ok(prop.to_vec())81 }8283 fn eth_set_sponsor(&mut self, caller: caller, sponsor: address) -> Result<void> {84 check_is_owner(caller, self)?;8586 let sponsor = T::CrossAccountId::from_eth(sponsor);87 self.set_sponsor(sponsor.as_sub().clone());88 save(self);89 Ok(())90 }9192 fn eth_confirm_sponsorship(&mut self, caller: caller) -> Result<void> {93 let caller = T::CrossAccountId::from_eth(caller);94 if !self.confirm_sponsorship(caller.as_sub()) {95 return Err(Error::Revert("Caller is not set as sponsor".into()));96 }97 save(self);98 Ok(())99 }100101 #[solidity(rename_selector = "setLimit")]102 fn set_int_limit(&mut self, caller: caller, limit: string, value: uint32) -> Result<void> {103 check_is_owner(caller, self)?;104 let mut limits = self.limits.clone();105106 match limit.as_str() {107 "accountTokenOwnershipLimit" => {108 limits.account_token_ownership_limit = Some(value);109 }110 "sponsoredDataSize" => {111 limits.sponsored_data_size = Some(value);112 }113 "sponsoredDataRateLimit" => {114 limits.sponsored_data_rate_limit = Some(SponsoringRateLimit::Blocks(value));115 }116 "tokenLimit" => {117 limits.token_limit = Some(value);118 }119 "sponsorTransferTimeout" => {120 limits.sponsor_transfer_timeout = Some(value);121 }122 "sponsorApproveTimeout" => {123 limits.sponsor_approve_timeout = Some(value);124 }125 _ => {126 return Err(Error::Revert(format!(127 "Unknown integer limit \"{}\"",128 limit129 )))130 }131 }132 self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)133 .map_err(dispatch_to_evm::<T>)?;134 save(self);135 Ok(())136 }137138 #[solidity(rename_selector = "setLimit")]139 fn set_bool_limit(&mut self, caller: caller, limit: string, value: bool) -> Result<void> {140 check_is_owner(caller, self)?;141 let mut limits = self.limits.clone();142143 match limit.as_str() {144 "ownerCanTransfer" => {145 limits.owner_can_transfer = Some(value);146 }147 "ownerCanDestroy" => {148 limits.owner_can_destroy = Some(value);149 }150 "transfersEnabled" => {151 limits.transfers_enabled = Some(value);152 }153 _ => {154 return Err(Error::Revert(format!(155 "Unknown boolean limit \"{}\"",156 limit157 )))158 }159 }160 self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)161 .map_err(dispatch_to_evm::<T>)?;162 save(self);163 Ok(())164 }165166 fn contract_address(&self, _caller: caller) -> Result<address> {167 Ok(crate::eth::collection_id_to_address(self.id))168 }169}170171fn check_is_owner<T: Config>(caller: caller, collection: &CollectionHandle<T>) -> Result<()> {172 let caller = T::CrossAccountId::from_eth(caller);173 collection174 .check_is_owner(&caller)175 .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;176 Ok(())177}178179fn save<T: Config>(collection: &CollectionHandle<T>) {180 <crate::CollectionById<T>>::insert(collection.id, collection.collection.clone());181}182183pub fn token_uri_key() -> up_data_structs::PropertyKey {184 b"tokenURI"185 .to_vec()186 .try_into()187 .expect("length < limit; qed")188}