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

difftreelog

source

pallets/fungible/src/erc.rs11.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/>.1617//! ERC-20 standart support implementation.1819extern crate alloc;20use core::{21	char::{decode_utf16, REPLACEMENT_CHARACTER},22	convert::TryInto,23};2425use evm_coder::{abi::AbiType, generate_stubgen, solidity_interface, types::*, AbiCoder, ToLog};26use pallet_common::{27	erc::{CollectionCall, CommonEvmHandler, PrecompileResult},28	eth::CrossAddress,29	CollectionHandle, CommonWeightInfo as _,30};31use pallet_evm::{account::CrossAccountId, PrecompileHandle};32use pallet_evm_coder_substrate::{33	call, dispatch_to_evm,34	execution::{PreDispatch, Result},35	frontier_contract, SubstrateRecorder,36};37use pallet_structure::{weights::WeightInfo as _, SelfWeightOf as StructureWeight};38use sp_core::{Get, U256};39use sp_std::vec::Vec;40use up_data_structs::{budget::Budget, CollectionMode};4142use crate::{43	common::CommonWeights, weights::WeightInfo, Allowance, Balance, Config, FungibleHandle, Pallet,44	SelfWeightOf, TotalSupply,45};4647frontier_contract! {48	macro_rules! FungibleHandle_result {...}49	impl<T: Config> Contract for FungibleHandle<T> {...}50}5152#[derive(ToLog)]53pub enum ERC20Events {54	Transfer {55		#[indexed]56		from: Address,57		#[indexed]58		to: Address,59		value: U256,60	},61	Approval {62		#[indexed]63		owner: Address,64		#[indexed]65		spender: Address,66		value: U256,67	},68}6970#[derive(AbiCoder, Debug)]71pub struct AmountForAddress {72	to: Address,73	amount: U256,74}7576fn nesting_budget<T: Config>(recorder: &SubstrateRecorder<T>) -> impl Budget + '_ {77	recorder.weight_calls_budget(<StructureWeight<T>>::find_parent())78}7980#[solidity_interface(name = ERC20, events(ERC20Events), enum(derive(PreDispatch)), enum_attr(weight), expect_selector = 0x942e8b22)]81impl<T: Config> FungibleHandle<T> {82	fn name(&self) -> Result<String> {83		Ok(decode_utf16(self.name.iter().copied())84			.map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))85			.collect::<String>())86	}87	fn symbol(&self) -> Result<String> {88		Ok(String::from_utf8_lossy(&self.token_prefix).into())89	}90	fn total_supply(&self) -> Result<U256> {91		self.consume_store_reads(1)?;92		Ok(<TotalSupply<T>>::get(self.id).into())93	}9495	fn decimals(&self) -> Result<u8> {96		Ok(if let CollectionMode::Fungible(decimals) = &self.mode {97			*decimals98		} else {99			unreachable!()100		})101	}102	fn balance_of(&self, owner: Address) -> Result<U256> {103		self.consume_store_reads(1)?;104		let owner = T::CrossAccountId::from_eth(owner);105		let balance = <Balance<T>>::get((self.id, owner));106		Ok(balance.into())107	}108	#[weight(<CommonWeights<T>>::transfer())]109	fn transfer(&mut self, caller: Caller, to: Address, amount: U256) -> Result<bool> {110		let caller = T::CrossAccountId::from_eth(caller);111		let to = T::CrossAccountId::from_eth(to);112		let amount = amount.try_into().map_err(|_| "amount overflow")?;113114		<Pallet<T>>::transfer(self, &caller, &to, amount, &nesting_budget(&self.recorder))115			.map_err(|e| dispatch_to_evm::<T>(e.error))?;116		Ok(true)117	}118119	#[weight(<CommonWeights<T>>::transfer_from())]120	fn transfer_from(121		&mut self,122		caller: Caller,123		from: Address,124		to: Address,125		amount: U256,126	) -> Result<bool> {127		let caller = T::CrossAccountId::from_eth(caller);128		let from = T::CrossAccountId::from_eth(from);129		let to = T::CrossAccountId::from_eth(to);130		let amount = amount.try_into().map_err(|_| "amount overflow")?;131132		<Pallet<T>>::transfer_from(133			self,134			&caller,135			&from,136			&to,137			amount,138			&nesting_budget(&self.recorder),139		)140		.map_err(|e| dispatch_to_evm::<T>(e.error))?;141		Ok(true)142	}143	#[weight(<SelfWeightOf<T>>::approve())]144	fn approve(&mut self, caller: Caller, spender: Address, amount: U256) -> Result<bool> {145		let caller = T::CrossAccountId::from_eth(caller);146		let spender = T::CrossAccountId::from_eth(spender);147		let amount = amount.try_into().map_err(|_| "amount overflow")?;148149		<Pallet<T>>::set_allowance(self, &caller, &spender, amount)150			.map_err(dispatch_to_evm::<T>)?;151		Ok(true)152	}153	fn allowance(&self, owner: Address, spender: Address) -> Result<U256> {154		self.consume_store_reads(1)?;155		let owner = T::CrossAccountId::from_eth(owner);156		let spender = T::CrossAccountId::from_eth(spender);157158		Ok(<Allowance<T>>::get((self.id, owner, spender)).into())159	}160}161162#[solidity_interface(name = ERC20Mintable, enum(derive(PreDispatch)), enum_attr(weight))]163impl<T: Config> FungibleHandle<T> {164	/// Mint tokens for `to` account.165	/// @param to account that will receive minted tokens166	/// @param amount amount of tokens to mint167	#[weight(<SelfWeightOf<T>>::create_item())]168	fn mint(&mut self, caller: Caller, to: Address, amount: U256) -> Result<bool> {169		let caller = T::CrossAccountId::from_eth(caller);170		let to = T::CrossAccountId::from_eth(to);171		let amount = amount.try_into().map_err(|_| "amount overflow")?;172173		<Pallet<T>>::create_item(self, &caller, (to, amount), &nesting_budget(&self.recorder))174			.map_err(dispatch_to_evm::<T>)?;175		Ok(true)176	}177}178179#[solidity_interface(name = ERC20UniqueExtensions, enum(derive(PreDispatch)), enum_attr(weight))]180impl<T: Config> FungibleHandle<T>181where182	T::AccountId: From<[u8; 32]>,183{184	/// @dev Function to check the amount of tokens that an owner allowed to a spender.185	/// @param owner crossAddress The address which owns the funds.186	/// @param spender crossAddress The address which will spend the funds.187	/// @return A uint256 specifying the amount of tokens still available for the spender.188	fn allowance_cross(&self, owner: CrossAddress, spender: CrossAddress) -> Result<U256> {189		let owner = owner.into_sub_cross_account::<T>()?;190		let spender = spender.into_sub_cross_account::<T>()?;191192		Ok(<Allowance<T>>::get((self.id, owner, spender)).into())193	}194195	/// @notice A description for the collection.196	fn description(&self) -> String {197		decode_utf16(self.description.iter().copied())198			.map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))199			.collect::<String>()200	}201202	#[weight(<SelfWeightOf<T>>::create_item())]203	fn mint_cross(&mut self, caller: Caller, to: CrossAddress, amount: U256) -> Result<bool> {204		let caller = T::CrossAccountId::from_eth(caller);205		let to = to.into_sub_cross_account::<T>()?;206		let amount = amount.try_into().map_err(|_| "amount overflow")?;207208		<Pallet<T>>::create_item(self, &caller, (to, amount), &nesting_budget(&self.recorder))209			.map_err(dispatch_to_evm::<T>)?;210		Ok(true)211	}212213	#[weight(<SelfWeightOf<T>>::approve())]214	fn approve_cross(215		&mut self,216		caller: Caller,217		spender: CrossAddress,218		amount: U256,219	) -> Result<bool> {220		let caller = T::CrossAccountId::from_eth(caller);221		let spender = spender.into_sub_cross_account::<T>()?;222		let amount = amount.try_into().map_err(|_| "amount overflow")?;223224		<Pallet<T>>::set_allowance(self, &caller, &spender, amount)225			.map_err(dispatch_to_evm::<T>)?;226		Ok(true)227	}228229	/// Burn tokens from account230	/// @dev Function that burns an `amount` of the tokens of a given account,231	/// deducting from the sender's allowance for said account.232	/// @param from The account whose tokens will be burnt.233	/// @param amount The amount that will be burnt.234	#[solidity(hide)]235	#[weight(<SelfWeightOf<T>>::burn_from())]236	fn burn_from(&mut self, caller: Caller, from: Address, amount: U256) -> Result<bool> {237		let caller = T::CrossAccountId::from_eth(caller);238		let from = T::CrossAccountId::from_eth(from);239		let amount = amount.try_into().map_err(|_| "amount overflow")?;240241		<Pallet<T>>::burn_from(242			self,243			&caller,244			&from,245			amount,246			&nesting_budget(&self.recorder),247		)248		.map_err(dispatch_to_evm::<T>)?;249		Ok(true)250	}251252	/// Burn tokens from account253	/// @dev Function that burns an `amount` of the tokens of a given account,254	/// deducting from the sender's allowance for said account.255	/// @param from The account whose tokens will be burnt.256	/// @param amount The amount that will be burnt.257	#[weight(<SelfWeightOf<T>>::burn_from())]258	fn burn_from_cross(259		&mut self,260		caller: Caller,261		from: CrossAddress,262		amount: U256,263	) -> Result<bool> {264		let caller = T::CrossAccountId::from_eth(caller);265		let from = from.into_sub_cross_account::<T>()?;266		let amount = amount.try_into().map_err(|_| "amount overflow")?;267268		<Pallet<T>>::burn_from(269			self,270			&caller,271			&from,272			amount,273			&nesting_budget(&self.recorder),274		)275		.map_err(dispatch_to_evm::<T>)?;276		Ok(true)277	}278279	/// Mint tokens for multiple accounts.280	/// @param amounts array of pairs of account address and amount281	#[weight(<SelfWeightOf<T>>::create_multiple_items_ex(amounts.len() as u32))]282	fn mint_bulk(&mut self, caller: Caller, amounts: Vec<AmountForAddress>) -> Result<bool> {283		let caller = T::CrossAccountId::from_eth(caller);284		let amounts = amounts285			.into_iter()286			.map(|AmountForAddress { to, amount }| {287				Ok((288					T::CrossAccountId::from_eth(to),289					amount.try_into().map_err(|_| "amount overflow")?,290				))291			})292			.collect::<Result<_>>()?;293294		<Pallet<T>>::create_multiple_items(self, &caller, amounts, &nesting_budget(&self.recorder))295			.map_err(dispatch_to_evm::<T>)?;296		Ok(true)297	}298299	#[weight(<CommonWeights<T>>::transfer())]300	fn transfer_cross(&mut self, caller: Caller, to: CrossAddress, amount: U256) -> Result<bool> {301		let caller = T::CrossAccountId::from_eth(caller);302		let to = to.into_sub_cross_account::<T>()?;303		let amount = amount.try_into().map_err(|_| "amount overflow")?;304305		<Pallet<T>>::transfer(self, &caller, &to, amount, &nesting_budget(&self.recorder))306			.map_err(|_| "transfer error")?;307		Ok(true)308	}309310	#[weight(<CommonWeights<T>>::transfer_from())]311	fn transfer_from_cross(312		&mut self,313		caller: Caller,314		from: CrossAddress,315		to: CrossAddress,316		amount: U256,317	) -> Result<bool> {318		let caller = T::CrossAccountId::from_eth(caller);319		let from = from.into_sub_cross_account::<T>()?;320		let to = to.into_sub_cross_account::<T>()?;321		let amount = amount.try_into().map_err(|_| "amount overflow")?;322323		<Pallet<T>>::transfer_from(324			self,325			&caller,326			&from,327			&to,328			amount,329			&nesting_budget(&self.recorder),330		)331		.map_err(|e| dispatch_to_evm::<T>(e.error))?;332		Ok(true)333	}334335	/// @notice Returns collection helper contract address336	fn collection_helper_address(&self) -> Result<Address> {337		Ok(T::ContractAddress::get())338	}339340	/// @notice Balance of account341	/// @param owner An cross address for whom to query the balance342	/// @return The number of fingibles owned by `owner`, possibly zero343	fn balance_of_cross(&self, owner: CrossAddress) -> Result<U256> {344		self.consume_store_reads(1)?;345		let balance = <Balance<T>>::get((self.id, owner.into_sub_cross_account::<T>()?));346		Ok(balance.into())347	}348}349350#[solidity_interface(351	name = UniqueFungible,352	is(353		ERC20,354		ERC20Mintable,355		ERC20UniqueExtensions,356		Collection(via(common_mut returns CollectionHandle<T>)),357	),358	enum(derive(PreDispatch))359)]360impl<T: Config> FungibleHandle<T> where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]> {}361362generate_stubgen!(gen_impl, UniqueFungibleCall<()>, true);363generate_stubgen!(gen_iface, UniqueFungibleCall<()>, false);364365impl<T: Config> CommonEvmHandler for FungibleHandle<T>366where367	T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,368{369	const CODE: &'static [u8] = include_bytes!("./stubs/UniqueFungible.raw");370371	fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {372		call::<T, UniqueFungibleCall<T>, _, _>(handle, self)373	}374}