git.delta.rocks / unique-network / refs/commits / 275d4ae154a4

difftreelog

source

pallets/common/src/erc.rs10.7 KiBsourcehistory
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617use evm_coder::{18	solidity_interface, solidity, ToLog,19	types::*,20	execution::{Result, Error},21};22pub use pallet_evm::{PrecompileOutput, PrecompileResult, PrecompileHandle, account::CrossAccountId};23use pallet_evm_coder_substrate::dispatch_to_evm;24use sp_std::vec::Vec;25use up_data_structs::{26	Property, SponsoringRateLimit, NestingRule, OwnerRestrictedSet, AccessMode,27	CollectionPermissions,28};29use alloc::format;3031use crate::{Pallet, CollectionHandle, Config, CollectionProperties};3233#[derive(ToLog)]34pub enum CollectionHelpersEvents {35	CollectionCreated {36		#[indexed]37		owner: address,38		#[indexed]39		collection_id: address,40	},41}4243/// Does not always represent a full collection, for RFT it is either44/// collection (Implementing ERC721), or specific collection token (Implementing ERC20)45pub trait CommonEvmHandler {46	const CODE: &'static [u8];4748	fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult>;49}5051#[solidity_interface(name = "Collection")]52impl<T: Config> CollectionHandle<T>53where54	T::AccountId: From<[u8; 32]>,55{56	fn set_collection_property(57		&mut self,58		caller: caller,59		key: string,60		value: bytes,61	) -> Result<void> {62		let caller = T::CrossAccountId::from_eth(caller);63		let key = <Vec<u8>>::from(key)64			.try_into()65			.map_err(|_| "key too large")?;66		let value = value.try_into().map_err(|_| "value too large")?;6768		<Pallet<T>>::set_collection_property(self, &caller, Property { key, value })69			.map_err(dispatch_to_evm::<T>)70	}7172	fn delete_collection_property(&mut self, caller: caller, key: string) -> Result<()> {73		let caller = T::CrossAccountId::from_eth(caller);74		let key = <Vec<u8>>::from(key)75			.try_into()76			.map_err(|_| "key too large")?;7778		<Pallet<T>>::delete_collection_property(self, &caller, key).map_err(dispatch_to_evm::<T>)79	}8081	/// Throws error if key not found82	fn collection_property(&self, key: string) -> Result<bytes> {83		let key = <Vec<u8>>::from(key)84			.try_into()85			.map_err(|_| "key too large")?;8687		let props = <CollectionProperties<T>>::get(self.id);88		let prop = props.get(&key).ok_or("key not found")?;8990		Ok(prop.to_vec())91	}9293	fn set_collection_sponsor(&mut self, caller: caller, sponsor: address) -> Result<void> {94		check_is_owner_or_admin(caller, self)?;9596		let sponsor = T::CrossAccountId::from_eth(sponsor);97		self.set_sponsor(sponsor.as_sub().clone())98			.map_err(dispatch_to_evm::<T>)?;99		save(self)100	}101102	fn confirm_collection_sponsorship(&mut self, caller: caller) -> Result<void> {103		let caller = T::CrossAccountId::from_eth(caller);104		if !self105			.confirm_sponsorship(caller.as_sub())106			.map_err(dispatch_to_evm::<T>)?107		{108			return Err("caller is not set as sponsor".into());109		}110		save(self)111	}112113	#[solidity(rename_selector = "setCollectionLimit")]114	fn set_int_limit(&mut self, caller: caller, limit: string, value: uint32) -> Result<void> {115		check_is_owner_or_admin(caller, self)?;116		let mut limits = self.limits.clone();117118		match limit.as_str() {119			"accountTokenOwnershipLimit" => {120				limits.account_token_ownership_limit = Some(value);121			}122			"sponsoredDataSize" => {123				limits.sponsored_data_size = Some(value);124			}125			"sponsoredDataRateLimit" => {126				limits.sponsored_data_rate_limit = Some(SponsoringRateLimit::Blocks(value));127			}128			"tokenLimit" => {129				limits.token_limit = Some(value);130			}131			"sponsorTransferTimeout" => {132				limits.sponsor_transfer_timeout = Some(value);133			}134			"sponsorApproveTimeout" => {135				limits.sponsor_approve_timeout = Some(value);136			}137			_ => {138				return Err(Error::Revert(format!(139					"unknown integer limit \"{}\"",140					limit141				)))142			}143		}144		self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)145			.map_err(dispatch_to_evm::<T>)?;146		save(self)147	}148149	#[solidity(rename_selector = "setCollectionLimit")]150	fn set_bool_limit(&mut self, caller: caller, limit: string, value: bool) -> Result<void> {151		check_is_owner_or_admin(caller, self)?;152		let mut limits = self.limits.clone();153154		match limit.as_str() {155			"ownerCanTransfer" => {156				limits.owner_can_transfer = Some(value);157			}158			"ownerCanDestroy" => {159				limits.owner_can_destroy = Some(value);160			}161			"transfersEnabled" => {162				limits.transfers_enabled = Some(value);163			}164			_ => {165				return Err(Error::Revert(format!(166					"unknown boolean limit \"{}\"",167					limit168				)))169			}170		}171		self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)172			.map_err(dispatch_to_evm::<T>)?;173		save(self)174	}175176	fn contract_address(&self, _caller: caller) -> Result<address> {177		Ok(crate::eth::collection_id_to_address(self.id))178	}179180	fn add_collection_admin_substrate(&self, caller: caller, new_admin: uint256) -> Result<void> {181		let caller = T::CrossAccountId::from_eth(caller);182		let mut new_admin_arr: [u8; 32] = Default::default();183		new_admin.to_big_endian(&mut new_admin_arr);184		let account_id = T::AccountId::from(new_admin_arr);185		let new_admin = T::CrossAccountId::from_sub(account_id);186		<Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;187		Ok(())188	}189190	fn remove_collection_admin_substrate(191		&self,192		caller: caller,193		new_admin: uint256,194	) -> Result<void> {195		let caller = T::CrossAccountId::from_eth(caller);196		let mut new_admin_arr: [u8; 32] = Default::default();197		new_admin.to_big_endian(&mut new_admin_arr);198		let account_id = T::AccountId::from(new_admin_arr);199		let new_admin = T::CrossAccountId::from_sub(account_id);200		<Pallet<T>>::toggle_admin(self, &caller, &new_admin, false)201			.map_err(dispatch_to_evm::<T>)?;202		Ok(())203	}204205	fn add_collection_admin(&self, caller: caller, new_admin: address) -> Result<void> {206		let caller = T::CrossAccountId::from_eth(caller);207		let new_admin = T::CrossAccountId::from_eth(new_admin);208		<Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;209		Ok(())210	}211212	fn remove_collection_admin(&self, caller: caller, admin: address) -> Result<void> {213		let caller = T::CrossAccountId::from_eth(caller);214		let admin = T::CrossAccountId::from_eth(admin);215		<Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;216		Ok(())217	}218219	#[solidity(rename_selector = "setCollectionNesting")]220	fn set_nesting_bool(&mut self, caller: caller, enable: bool) -> Result<void> {221		check_is_owner_or_admin(caller, self)?;222		let permissions = CollectionPermissions {223			nesting: Some(match enable {224				false => NestingRule::Disabled,225				true => NestingRule::Owner,226			}),227			..Default::default()228		};229		self.collection.permissions = <Pallet<T>>::clamp_permissions(230			self.collection.mode.clone(),231			&self.collection.permissions,232			permissions,233		)234		.map_err(dispatch_to_evm::<T>)?;235236		save(self)237	}238239	#[solidity(rename_selector = "setCollectionNesting")]240	fn set_nesting(241		&mut self,242		caller: caller,243		enable: bool,244		collections: Vec<address>,245	) -> Result<void> {246		if collections.is_empty() {247			return Err("no addresses provided".into());248		}249		if collections.len() >= OwnerRestrictedSet::bound() {250			return Err(Error::Revert(format!(251				"out of bound: {} >= {}",252				collections.len(),253				OwnerRestrictedSet::bound()254			)));255		}256		check_is_owner_or_admin(caller, self)?;257		let permissions = CollectionPermissions {258			nesting: Some(match enable {259				false => NestingRule::Disabled,260				true => {261					let mut bv = OwnerRestrictedSet::new();262					for i in collections {263						bv.try_insert(crate::eth::map_eth_to_id(&i).ok_or_else(|| {264							Error::Revert("can't convert address into collection id".into())265						})?)266						.map_err(|e| Error::Revert(format!("{:?}", e)))?;267					}268					NestingRule::OwnerRestricted(bv)269				}270			}),271			..Default::default()272		};273		self.collection.permissions = <Pallet<T>>::clamp_permissions(274			self.collection.mode.clone(),275			&self.collection.permissions,276			permissions,277		)278		.map_err(dispatch_to_evm::<T>)?;279280		save(self)281	}282283	fn set_collection_access(&mut self, caller: caller, mode: uint8) -> Result<void> {284		check_is_owner_or_admin(caller, self)?;285		let permissions = CollectionPermissions {286			access: Some(match mode {287				0 => AccessMode::Normal,288				1 => AccessMode::AllowList,289				_ => return Err("not supported access mode".into()),290			}),291			..Default::default()292		};293		self.collection.permissions = <Pallet<T>>::clamp_permissions(294			self.collection.mode.clone(),295			&self.collection.permissions,296			permissions,297		)298		.map_err(dispatch_to_evm::<T>)?;299300		save(self)301	}302303	fn add_to_collection_allow_list(&self, caller: caller, user: address) -> Result<void> {304		let caller = T::CrossAccountId::from_eth(caller);305		let user = T::CrossAccountId::from_eth(user);306		<Pallet<T>>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;307		Ok(())308	}309310	fn remove_from_collection_allow_list(&self, caller: caller, user: address) -> Result<void> {311		let caller = T::CrossAccountId::from_eth(caller);312		let user = T::CrossAccountId::from_eth(user);313		<Pallet<T>>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;314		Ok(())315	}316317	fn set_collection_mint_mode(&mut self, caller: caller, mode: bool) -> Result<void> {318		check_is_owner_or_admin(caller, self)?;319		let permissions = CollectionPermissions {320			mint_mode: Some(mode),321			..Default::default()322		};323		self.collection.permissions = <Pallet<T>>::clamp_permissions(324			self.collection.mode.clone(),325			&self.collection.permissions,326			permissions,327		)328		.map_err(dispatch_to_evm::<T>)?;329330		save(self)331	}332}333334fn check_is_owner_or_admin<T: Config>(335	caller: caller,336	collection: &CollectionHandle<T>,337) -> Result<T::CrossAccountId> {338	let caller = T::CrossAccountId::from_eth(caller);339	collection340		.check_is_owner_or_admin(&caller)341		.map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;342	Ok(caller)343}344345fn save<T: Config>(collection: &CollectionHandle<T>) -> Result<void> {346	// TODO possibly delete for the lack of transaction347	collection348		.check_is_internal()349		.map_err(dispatch_to_evm::<T>)?;350	<crate::CollectionById<T>>::insert(collection.id, collection.collection.clone());351	Ok(())352}353354pub fn token_uri_key() -> up_data_structs::PropertyKey {355	b"tokenURI"356		.to_vec()357		.try_into()358		.expect("length < limit; qed")359}