git.delta.rocks / unique-network / refs/commits / db8b9bab3341

difftreelog

source

pallets/common/src/erc.rs10.2 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<()> {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		Ok(())233	}234235	#[solidity(rename_selector = "setNesting")]236	fn set_nesting(237		&mut self,238		caller: caller,239		enable: bool,240		collections: Vec<address>,241	) -> Result<void> {242		if collections.is_empty() {243			return Err("No addresses provided".into());244		}245		if collections.len() >= OwnerRestrictedSet::bound() {246			return Err(Error::Revert(format!(247				"Out of bound: {} >= {}",248				collections.len(),249				OwnerRestrictedSet::bound()250			)));251		}252		check_is_owner_or_admin(caller, self)?;253		let permissions = CollectionPermissions {254			nesting: Some(match enable {255				false => NestingRule::Disabled,256				true => {257					let mut bv = OwnerRestrictedSet::new();258					for i in collections {259						bv.try_insert(crate::eth::map_eth_to_id(&i).ok_or_else(|| {260							Error::Revert("Can't convert address into collection id".into())261						})?)262						.map_err(|e| Error::Revert(format!("{:?}", e)))?;263					}264					NestingRule::OwnerRestricted(bv)265				}266			}),267			..Default::default()268		};269		self.collection.permissions = <Pallet<T>>::clamp_permissions(270			self.collection.mode.clone(),271			&self.collection.permissions,272			permissions,273		)274		.map_err(dispatch_to_evm::<T>)?;275		276		save(self)?;277		Ok(())278	}279280	fn set_collection_access(&mut self, caller: caller, mode: uint8) -> Result<void> {281		check_is_owner_or_admin(caller, self)?;282		self.collection.permissions.access = Some(match mode {283			0 => AccessMode::Normal,284			1 => AccessMode::AllowList,285			_ => return Err("Not supported access mode".into()),286		});287		save(self)?;288		Ok(())289	}290291	fn add_to_collection_allow_list(&self, caller: caller, user: address) -> Result<void> {292		let caller = T::CrossAccountId::from_eth(caller);293		let user = T::CrossAccountId::from_eth(user);294		<Pallet<T>>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;295		Ok(())296	}297298	fn remove_from_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, false).map_err(dispatch_to_evm::<T>)?;302		Ok(())303	}304305	fn set_mint_mode(&mut self, caller: caller, mode: bool) -> Result<void> {306		check_is_owner_or_admin(caller, self)?;307		self.collection.permissions.mint_mode = Some(mode);308		save(self)?;309		Ok(())310	}311}312313fn check_is_owner_or_admin<T: Config>(314	caller: caller,315	collection: &CollectionHandle<T>,316) -> Result<T::CrossAccountId> {317	let caller = T::CrossAccountId::from_eth(caller);318	collection319		.check_is_owner_or_admin(&caller)320		.map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;321	Ok(caller)322}323324fn save<T: Config>(collection: &CollectionHandle<T>) -> Result<void> {325	// TODO possibly delete for the lack of transaction326	collection327		.check_is_internal()328		.map_err(dispatch_to_evm::<T>)?;329	<crate::CollectionById<T>>::insert(collection.id, collection.collection.clone());330	Ok(())331}332333pub fn token_uri_key() -> up_data_structs::PropertyKey {334	b"tokenURI"335		.to_vec()336		.try_into()337		.expect("length < limit; qed")338}