git.delta.rocks / unique-network / refs/commits / 0dc93c7a2ef6

difftreelog

source

pallets/evm-contract-helpers/src/eth.rs8.0 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 core::marker::PhantomData;18use evm_coder::{abi::AbiWriter, execution::*, generate_stubgen, solidity_interface, types::*, ToLog};19use ethereum as _;20use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};21use pallet_evm::{22	ExitRevert, OnCreate, OnMethodCall, PrecompileResult, PrecompileFailure,23	account::CrossAccountId, Pallet as PalletEvm24};25use sp_core::H160;26use up_data_structs::{27	CreateCollectionData, MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH,28	MAX_COLLECTION_NAME_LENGTH,29};30use crate::{31	AllowlistEnabled, Config, Owner, Pallet, SponsorBasket, SponsoringRateLimit, SponsoringModeT,32};33use frame_support::traits::Get;34use up_sponsorship::SponsorshipHandler;35use sp_std::vec::Vec;36use alloc::format;3738struct ContractHelpers<T: Config>(SubstrateRecorder<T>);39impl<T: Config> WithRecorder<T> for ContractHelpers<T> {40	fn recorder(&self) -> &SubstrateRecorder<T> {41		&self.042	}4344	fn into_recorder(self) -> SubstrateRecorder<T> {45		self.046	}47}4849#[derive(ToLog)]50pub enum ContractHelperEvent {51	CollectionCreated {52		#[indexed]53		owner: address,54		#[indexed]55		collection_id: address,56	},57}5859#[solidity_interface(name = "ContractHelpers")]60impl<T: Config> ContractHelpers<T> {61	fn contract_owner(&self, contract_address: address) -> Result<address> {62		Ok(<Owner<T>>::get(contract_address))63	}6465	fn sponsoring_enabled(&self, contract_address: address) -> Result<bool> {66		Ok(<Pallet<T>>::sponsoring_mode(contract_address) != SponsoringModeT::Disabled)67	}6869	/// Deprecated70	fn toggle_sponsoring(71		&mut self,72		caller: caller,73		contract_address: address,74		enabled: bool,75	) -> Result<void> {76		<Pallet<T>>::ensure_owner(contract_address, caller)?;77		<Pallet<T>>::toggle_sponsoring(contract_address, enabled);78		Ok(())79	}8081	fn set_sponsoring_mode(82		&mut self,83		caller: caller,84		contract_address: address,85		mode: uint8,86	) -> Result<void> {87		<Pallet<T>>::ensure_owner(contract_address, caller)?;88		let mode = SponsoringModeT::from_eth(mode).ok_or("unknown mode")?;89		<Pallet<T>>::set_sponsoring_mode(contract_address, mode);90		Ok(())91	}9293	fn sponsoring_mode(&self, contract_address: address) -> Result<uint8> {94		Ok(<Pallet<T>>::sponsoring_mode(contract_address).to_eth())95	}9697	fn set_sponsoring_rate_limit(98		&mut self,99		caller: caller,100		contract_address: address,101		rate_limit: uint32,102	) -> Result<void> {103		<Pallet<T>>::ensure_owner(contract_address, caller)?;104		<Pallet<T>>::set_sponsoring_rate_limit(contract_address, rate_limit.into());105		Ok(())106	}107108	fn get_sponsoring_rate_limit(&self, contract_address: address) -> Result<uint32> {109		Ok(<SponsoringRateLimit<T>>::get(contract_address)110			.try_into()111			.map_err(|_| "rate limit > u32::MAX")?)112	}113114	fn allowed(&self, contract_address: address, user: address) -> Result<bool> {115		self.0.consume_sload()?;116		Ok(<Pallet<T>>::allowed(contract_address, user))117	}118119	fn allowlist_enabled(&self, contract_address: address) -> Result<bool> {120		Ok(<AllowlistEnabled<T>>::get(contract_address))121	}122123	fn toggle_allowlist(124		&mut self,125		caller: caller,126		contract_address: address,127		enabled: bool,128	) -> Result<void> {129		<Pallet<T>>::ensure_owner(contract_address, caller)?;130		<Pallet<T>>::toggle_allowlist(contract_address, enabled);131		Ok(())132	}133134	fn toggle_allowed(135		&mut self,136		caller: caller,137		contract_address: address,138		user: address,139		allowed: bool,140	) -> Result<void> {141		<Pallet<T>>::ensure_owner(contract_address, caller)?;142		<Pallet<T>>::toggle_allowed(contract_address, user, allowed);143		Ok(())144	}145146	fn create_721_collection(147		&self,148		caller: caller,149		name: string,150		description: string,151		token_prefix: string,152	) -> Result<address> {153		let caller = T::CrossAccountId::from_eth(caller);154		let name = name155			.encode_utf16()156			.collect::<Vec<u16>>()157			.try_into()158			.map_err(|_| error_feild_too_long("name", MAX_COLLECTION_NAME_LENGTH))?;159		let description = description160			.encode_utf16()161			.collect::<Vec<u16>>()162			.try_into()163			.map_err(|_| error_feild_too_long("description", MAX_COLLECTION_DESCRIPTION_LENGTH))?;164		let token_prefix = token_prefix165			.into_bytes()166			.try_into()167			.map_err(|_| error_feild_too_long("token_prefix", MAX_TOKEN_PREFIX_LENGTH))?;168169		let data = CreateCollectionData {170			name,171			description,172			token_prefix,173			..Default::default()174		};175176		let collection_id =177			<pallet_nonfungible::Pallet<T>>::init_collection(caller.as_sub().clone(), data)178				.map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;179180		let address = pallet_common::eth::collection_id_to_address(collection_id);181182		<PalletEvm<T>>::deposit_log(183			ContractHelperEvent::CollectionCreated {184				owner: *caller.as_eth(),185				collection_id: address,186			}187			.to_log(address),188		);189		Ok(address)190	}191}192193fn error_feild_too_long(feild: &str, bound: u32) -> Error {194	Error::Revert(format!("{} is too long. Max length is {}.", feild, bound))195}196197pub struct HelpersOnMethodCall<T: Config>(PhantomData<*const T>);198impl<T: Config> OnMethodCall<T> for HelpersOnMethodCall<T> {199	fn is_reserved(contract: &sp_core::H160) -> bool {200		contract == &T::ContractAddress::get()201	}202203	fn is_used(contract: &sp_core::H160) -> bool {204		contract == &T::ContractAddress::get()205	}206207	fn call(208		source: &sp_core::H160,209		target: &sp_core::H160,210		gas_left: u64,211		input: &[u8],212		value: sp_core::U256,213	) -> Option<PrecompileResult> {214		// TODO: Extract to another OnMethodCall handler215		if <AllowlistEnabled<T>>::get(target) && !<Pallet<T>>::allowed(*target, *source) {216			return Some(Err(PrecompileFailure::Revert {217				exit_status: ExitRevert::Reverted,218				cost: 0,219				output: {220					let mut writer = AbiWriter::new_call(evm_coder::fn_selector!(Error(string)));221					writer.string("Target contract is allowlisted");222					writer.finish()223				},224			}));225		}226227		if target != &T::ContractAddress::get() {228			return None;229		}230231		let helpers = ContractHelpers::<T>(SubstrateRecorder::<T>::new(gas_left));232		pallet_evm_coder_substrate::call(*source, helpers, value, input)233	}234235	fn get_code(contract: &sp_core::H160) -> Option<Vec<u8>> {236		(contract == &T::ContractAddress::get())237			.then(|| include_bytes!("./stubs/ContractHelpers.raw").to_vec())238	}239}240241pub struct HelpersOnCreate<T: Config>(PhantomData<*const T>);242impl<T: Config> OnCreate<T> for HelpersOnCreate<T> {243	fn on_create(owner: H160, contract: H160) {244		<Owner<T>>::insert(contract, owner);245	}246}247248pub struct HelpersContractSponsoring<T: Config>(PhantomData<*const T>);249impl<T: Config> SponsorshipHandler<T::CrossAccountId, (H160, Vec<u8>)>250	for HelpersContractSponsoring<T>251{252	fn get_sponsor(who: &T::CrossAccountId, call: &(H160, Vec<u8>)) -> Option<T::CrossAccountId> {253		let mode = <Pallet<T>>::sponsoring_mode(call.0);254		if mode == SponsoringModeT::Disabled {255			return None;256		}257258		if mode == SponsoringModeT::Allowlisted && !<Pallet<T>>::allowed(call.0, *who.as_eth()) {259			return None;260		}261		let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;262263		if let Some(last_tx_block) = <SponsorBasket<T>>::get(&call.0, who.as_eth()) {264			let limit = <SponsoringRateLimit<T>>::get(&call.0);265266			let timeout = last_tx_block + limit;267			if block_number < timeout {268				return None;269			}270		}271272		<SponsorBasket<T>>::insert(&call.0, who.as_eth(), block_number);273274		let sponsor = T::CrossAccountId::from_eth(call.0);275		Some(sponsor)276	}277}278279generate_stubgen!(contract_helpers_impl, ContractHelpersCall<()>, true);280generate_stubgen!(contract_helpers_iface, ContractHelpersCall<()>, false);