git.delta.rocks / unique-network / refs/commits / 84e2b3d75476

difftreelog

source

pallets/common/src/erc.rs10.6 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(&mut self, caller: caller, key: string, value: bytes) -> Result<void> {57		let caller = T::CrossAccountId::from_eth(caller);58		let key = <Vec<u8>>::from(key)59			.try_into()60			.map_err(|_| "key too large")?;61		let value = value.try_into().map_err(|_| "value too large")?;6263		<Pallet<T>>::set_collection_property(self, &caller, Property { key, value })64			.map_err(dispatch_to_evm::<T>)65	}6667	fn delete_collection_property(&mut self, caller: caller, key: string) -> Result<()> {68		let caller = T::CrossAccountId::from_eth(caller);69		let key = <Vec<u8>>::from(key)70			.try_into()71			.map_err(|_| "key too large")?;7273		<Pallet<T>>::delete_collection_property(self, &caller, key).map_err(dispatch_to_evm::<T>)74	}7576	/// Throws error if key not found77	fn collection_property(&self, key: string) -> Result<bytes> {78		let key = <Vec<u8>>::from(key)79			.try_into()80			.map_err(|_| "key too large")?;8182		let props = <CollectionProperties<T>>::get(self.id);83		let prop = props.get(&key).ok_or("key not found")?;8485		Ok(prop.to_vec())86	}8788	fn set_collection_sponsor(&mut self, caller: caller, sponsor: address) -> Result<void> {89		check_is_owner_or_admin(caller, self)?;9091		let sponsor = T::CrossAccountId::from_eth(sponsor);92		self.set_sponsor(sponsor.as_sub().clone())93			.map_err(dispatch_to_evm::<T>)?;94		save(self)95	}9697	fn confirm_collection_sponsorship(&mut self, caller: caller) -> Result<void> {98		let caller = T::CrossAccountId::from_eth(caller);99		if !self100			.confirm_sponsorship(caller.as_sub())101			.map_err(dispatch_to_evm::<T>)?102		{103			return Err("Caller is not set as sponsor".into());104		}105		save(self)106	}107108	#[solidity(rename_selector = "setCollectionLimit")]109	fn set_int_limit(&mut self, caller: caller, limit: string, value: uint32) -> Result<void> {110		check_is_owner_or_admin(caller, self)?;111		let mut limits = self.limits.clone();112113		match limit.as_str() {114			"accountTokenOwnershipLimit" => {115				limits.account_token_ownership_limit = Some(value);116			}117			"sponsoredDataSize" => {118				limits.sponsored_data_size = Some(value);119			}120			"sponsoredDataRateLimit" => {121				limits.sponsored_data_rate_limit = Some(SponsoringRateLimit::Blocks(value));122			}123			"tokenLimit" => {124				limits.token_limit = Some(value);125			}126			"sponsorTransferTimeout" => {127				limits.sponsor_transfer_timeout = Some(value);128			}129			"sponsorApproveTimeout" => {130				limits.sponsor_approve_timeout = Some(value);131			}132			_ => {133				return Err(Error::Revert(format!(134					"Unknown integer limit \"{}\"",135					limit136				)))137			}138		}139		self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)140			.map_err(dispatch_to_evm::<T>)?;141		save(self)142	}143144	#[solidity(rename_selector = "setCollectionLimit")]145	fn set_bool_limit(&mut self, caller: caller, limit: string, value: bool) -> Result<void> {146		check_is_owner_or_admin(caller, self)?;147		let mut limits = self.limits.clone();148149		match limit.as_str() {150			"ownerCanTransfer" => {151				limits.owner_can_transfer = Some(value);152			}153			"ownerCanDestroy" => {154				limits.owner_can_destroy = Some(value);155			}156			"transfersEnabled" => {157				limits.transfers_enabled = Some(value);158			}159			_ => {160				return Err(Error::Revert(format!(161					"Unknown boolean limit \"{}\"",162					limit163				)))164			}165		}166		self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)167			.map_err(dispatch_to_evm::<T>)?;168		save(self)169	}170171	fn contract_address(&self, _caller: caller) -> Result<address> {172		Ok(crate::eth::collection_id_to_address(self.id))173	}174175	fn add_collection_admin_substrate(&self, caller: caller, new_admin: uint256) -> Result<void> {176		let caller = T::CrossAccountId::from_eth(caller);177		let mut new_admin_arr: [u8; 32] = Default::default();178		new_admin.to_big_endian(&mut new_admin_arr);179		let account_id = T::AccountId::from(new_admin_arr);180		let new_admin = T::CrossAccountId::from_sub(account_id);181		<Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;182		Ok(())183	}184185	fn remove_collection_admin_substrate(186		&self,187		caller: caller,188		new_admin: uint256,189	) -> Result<void> {190		let caller = T::CrossAccountId::from_eth(caller);191		let mut new_admin_arr: [u8; 32] = Default::default();192		new_admin.to_big_endian(&mut new_admin_arr);193		let account_id = T::AccountId::from(new_admin_arr);194		let new_admin = T::CrossAccountId::from_sub(account_id);195		<Pallet<T>>::toggle_admin(self, &caller, &new_admin, false)196			.map_err(dispatch_to_evm::<T>)?;197		Ok(())198	}199200	fn add_collection_admin(&self, caller: caller, new_admin: address) -> Result<void> {201		let caller = T::CrossAccountId::from_eth(caller);202		let new_admin = T::CrossAccountId::from_eth(new_admin);203		<Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;204		Ok(())205	}206207	fn remove_collection_admin(&self, caller: caller, admin: address) -> Result<void> {208		let caller = T::CrossAccountId::from_eth(caller);209		let admin = T::CrossAccountId::from_eth(admin);210		<Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;211		Ok(())212	}213214	#[solidity(rename_selector = "setNesting")]215	fn set_nesting_bool(&mut self, caller: caller, enable: bool) -> Result<void> {216		check_is_owner_or_admin(caller, self)?;217		let permissions = CollectionPermissions {218			nesting: Some(match enable {219				false => NestingRule::Disabled,220				true => NestingRule::Owner,221			}),222			..Default::default()223		};224		self.collection.permissions = <Pallet<T>>::clamp_permissions(225			self.collection.mode.clone(),226			&self.collection.permissions,227			permissions,228		)229		.map_err(dispatch_to_evm::<T>)?;230231		save(self)232	}233234	#[solidity(rename_selector = "setNesting")]235	fn set_nesting(236		&mut self,237		caller: caller,238		enable: bool,239		collections: Vec<address>,240	) -> Result<void> {241		if collections.is_empty() {242			return Err("No addresses provided".into());243		}244		if collections.len() >= OwnerRestrictedSet::bound() {245			return Err(Error::Revert(format!(246				"Out of bound: {} >= {}",247				collections.len(),248				OwnerRestrictedSet::bound()249			)));250		}251		check_is_owner_or_admin(caller, self)?;252		let permissions = CollectionPermissions {253			nesting: Some(match enable {254				false => NestingRule::Disabled,255				true => {256					let mut bv = OwnerRestrictedSet::new();257					for i in collections {258						bv.try_insert(crate::eth::map_eth_to_id(&i).ok_or_else(|| {259							Error::Revert("Can't convert address into collection id".into())260						})?)261						.map_err(|e| Error::Revert(format!("{:?}", e)))?;262					}263					NestingRule::OwnerRestricted(bv)264				}265			}),266			..Default::default()267		};268		self.collection.permissions = <Pallet<T>>::clamp_permissions(269			self.collection.mode.clone(),270			&self.collection.permissions,271			permissions,272		)273		.map_err(dispatch_to_evm::<T>)?;274		275		save(self)276	}277278	fn set_collection_access(&mut self, caller: caller, mode: uint8) -> Result<void> {279		check_is_owner_or_admin(caller, self)?;280		let permissions = CollectionPermissions{281			access: Some(match mode {282				0 => AccessMode::Normal,283				1 => AccessMode::AllowList,284				_ => return Err("Not supported access mode".into()),285			}),286			.. Default::default()287		};288		self.collection.permissions = <Pallet<T>>::clamp_permissions(289			self.collection.mode.clone(),290			&self.collection.permissions,291			permissions,292		)293		.map_err(dispatch_to_evm::<T>)?;294295		save(self)296	}297298	fn add_to_collection_allow_list(&self, caller: caller, user: address) -> Result<void> {299		let caller = T::CrossAccountId::from_eth(caller);300		let user = T::CrossAccountId::from_eth(user);301		<Pallet<T>>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;302		Ok(())303	}304305	fn remove_from_collection_allow_list(&self, caller: caller, user: address) -> Result<void> {306		let caller = T::CrossAccountId::from_eth(caller);307		let user = T::CrossAccountId::from_eth(user);308		<Pallet<T>>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;309		Ok(())310	}311312	fn set_mint_mode(&mut self, caller: caller, mode: bool) -> Result<void> {313		check_is_owner_or_admin(caller, self)?;314		let permissions = CollectionPermissions { mint_mode: Some(mode), .. Default::default() };315		self.collection.permissions = <Pallet<T>>::clamp_permissions(316			self.collection.mode.clone(),317			&self.collection.permissions,318			permissions,319		)320		.map_err(dispatch_to_evm::<T>)?;321322		save(self)323	}324}325326fn check_is_owner_or_admin<T: Config>(327	caller: caller,328	collection: &CollectionHandle<T>,329) -> Result<T::CrossAccountId> {330	let caller = T::CrossAccountId::from_eth(caller);331	collection332		.check_is_owner_or_admin(&caller)333		.map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;334	Ok(caller)335}336337fn save<T: Config>(collection: &CollectionHandle<T>) -> Result<void> {338	// TODO possibly delete for the lack of transaction339	collection340		.check_is_internal()341		.map_err(dispatch_to_evm::<T>)?;342	<crate::CollectionById<T>>::insert(collection.id, collection.collection.clone());343	Ok(())344}345346pub fn token_uri_key() -> up_data_structs::PropertyKey {347	b"tokenURI"348		.to_vec()349		.try_into()350		.expect("length < limit; qed")351}