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

difftreelog

source

pallets/fungible/src/erc.rs11.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/>.1617//! ERC-20 standart support implementation.1819extern crate alloc;20use core::char::{REPLACEMENT_CHARACTER, decode_utf16};21use core::convert::TryInto;22use evm_coder::AbiCoder;23use evm_coder::{24	abi::AbiType, ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*,25	weight,26};27use up_data_structs::CollectionMode;28use pallet_common::{29	CollectionHandle,30	erc::{CommonEvmHandler, PrecompileResult, CollectionCall},31	eth::CrossAddress,32};33use sp_std::vec::Vec;34use pallet_evm::{account::CrossAccountId, PrecompileHandle};35use pallet_evm_coder_substrate::{call, dispatch_to_evm};36use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};37use sp_core::{U256, Get};3839use crate::{40	Allowance, Balance, Config, FungibleHandle, Pallet, SelfWeightOf, TotalSupply,41	weights::WeightInfo,42};4344#[derive(ToLog)]45pub enum ERC20Events {46	Transfer {47		#[indexed]48		from: Address,49		#[indexed]50		to: Address,51		value: U256,52	},53	Approval {54		#[indexed]55		owner: Address,56		#[indexed]57		spender: Address,58		value: U256,59	},60}6162#[derive(AbiCoder, Debug)]63pub struct AmountForAddress {64	to: Address,65	amount: U256,66}6768#[solidity_interface(name = ERC20, events(ERC20Events), expect_selector = 0x942e8b22)]69impl<T: Config> FungibleHandle<T> {70	fn name(&self) -> Result<String> {71		Ok(decode_utf16(self.name.iter().copied())72			.map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))73			.collect::<String>())74	}75	fn symbol(&self) -> Result<String> {76		Ok(String::from_utf8_lossy(&self.token_prefix).into())77	}78	fn total_supply(&self) -> Result<U256> {79		self.consume_store_reads(1)?;80		Ok(<TotalSupply<T>>::get(self.id).into())81	}8283	fn decimals(&self) -> Result<u8> {84		Ok(if let CollectionMode::Fungible(decimals) = &self.mode {85			*decimals86		} else {87			unreachable!()88		})89	}90	fn balance_of(&self, owner: Address) -> Result<U256> {91		self.consume_store_reads(1)?;92		let owner = T::CrossAccountId::from_eth(owner);93		let balance = <Balance<T>>::get((self.id, owner));94		Ok(balance.into())95	}96	#[weight(<SelfWeightOf<T>>::transfer())]97	fn transfer(&mut self, caller: Caller, to: Address, amount: U256) -> Result<bool> {98		let caller = T::CrossAccountId::from_eth(caller);99		let to = T::CrossAccountId::from_eth(to);100		let amount = amount.try_into().map_err(|_| "amount overflow")?;101		let budget = self102			.recorder103			.weight_calls_budget(<StructureWeight<T>>::find_parent());104105		<Pallet<T>>::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?;106		Ok(true)107	}108109	#[weight(<SelfWeightOf<T>>::transfer_from())]110	fn transfer_from(111		&mut self,112		caller: Caller,113		from: Address,114		to: Address,115		amount: U256,116	) -> Result<bool> {117		let caller = T::CrossAccountId::from_eth(caller);118		let from = T::CrossAccountId::from_eth(from);119		let to = T::CrossAccountId::from_eth(to);120		let amount = amount.try_into().map_err(|_| "amount overflow")?;121		let budget = self122			.recorder123			.weight_calls_budget(<StructureWeight<T>>::find_parent());124125		<Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)126			.map_err(dispatch_to_evm::<T>)?;127		Ok(true)128	}129	#[weight(<SelfWeightOf<T>>::approve())]130	fn approve(&mut self, caller: Caller, spender: Address, amount: U256) -> Result<bool> {131		let caller = T::CrossAccountId::from_eth(caller);132		let spender = T::CrossAccountId::from_eth(spender);133		let amount = amount.try_into().map_err(|_| "amount overflow")?;134135		<Pallet<T>>::set_allowance(self, &caller, &spender, amount)136			.map_err(dispatch_to_evm::<T>)?;137		Ok(true)138	}139	fn allowance(&self, owner: Address, spender: Address) -> Result<U256> {140		self.consume_store_reads(1)?;141		let owner = T::CrossAccountId::from_eth(owner);142		let spender = T::CrossAccountId::from_eth(spender);143144		Ok(<Allowance<T>>::get((self.id, owner, spender)).into())145	}146}147148#[solidity_interface(name = ERC20Mintable)]149impl<T: Config> FungibleHandle<T> {150	/// Mint tokens for `to` account.151	/// @param to account that will receive minted tokens152	/// @param amount amount of tokens to mint153	#[weight(<SelfWeightOf<T>>::create_item())]154	fn mint(&mut self, caller: Caller, to: Address, amount: U256) -> Result<bool> {155		let caller = T::CrossAccountId::from_eth(caller);156		let to = T::CrossAccountId::from_eth(to);157		let amount = amount.try_into().map_err(|_| "amount overflow")?;158		let budget = self159			.recorder160			.weight_calls_budget(<StructureWeight<T>>::find_parent());161		<Pallet<T>>::create_item(&self, &caller, (to, amount), &budget)162			.map_err(dispatch_to_evm::<T>)?;163		Ok(true)164	}165}166167#[solidity_interface(name = ERC20UniqueExtensions)]168impl<T: Config> FungibleHandle<T>169where170	T::AccountId: From<[u8; 32]>,171{172	/// @dev Function to check the amount of tokens that an owner allowed to a spender.173	/// @param owner crossAddress The address which owns the funds.174	/// @param spender crossAddress The address which will spend the funds.175	/// @return A uint256 specifying the amount of tokens still available for the spender.176	fn allowance_cross(&self, owner: CrossAddress, spender: CrossAddress) -> Result<U256> {177		let owner = owner.into_sub_cross_account::<T>()?;178		let spender = spender.into_sub_cross_account::<T>()?;179180		Ok(<Allowance<T>>::get((self.id, owner, spender)).into())181	}182183	/// @notice A description for the collection.184	fn description(&self) -> Result<String> {185		Ok(decode_utf16(self.description.iter().copied())186			.map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))187			.collect::<String>())188	}189190	#[weight(<SelfWeightOf<T>>::create_item())]191	fn mint_cross(&mut self, caller: Caller, to: CrossAddress, amount: U256) -> Result<bool> {192		let caller = T::CrossAccountId::from_eth(caller);193		let to = to.into_sub_cross_account::<T>()?;194		let amount = amount.try_into().map_err(|_| "amount overflow")?;195		let budget = self196			.recorder197			.weight_calls_budget(<StructureWeight<T>>::find_parent());198		<Pallet<T>>::create_item(&self, &caller, (to, amount), &budget)199			.map_err(dispatch_to_evm::<T>)?;200		Ok(true)201	}202203	#[weight(<SelfWeightOf<T>>::approve())]204	fn approve_cross(205		&mut self,206		caller: Caller,207		spender: CrossAddress,208		amount: U256,209	) -> Result<bool> {210		let caller = T::CrossAccountId::from_eth(caller);211		let spender = spender.into_sub_cross_account::<T>()?;212		let amount = amount.try_into().map_err(|_| "amount overflow")?;213214		<Pallet<T>>::set_allowance(self, &caller, &spender, amount)215			.map_err(dispatch_to_evm::<T>)?;216		Ok(true)217	}218219	/// Burn tokens from account220	/// @dev Function that burns an `amount` of the tokens of a given account,221	/// deducting from the sender's allowance for said account.222	/// @param from The account whose tokens will be burnt.223	/// @param amount The amount that will be burnt.224	#[solidity(hide)]225	#[weight(<SelfWeightOf<T>>::burn_from())]226	fn burn_from(&mut self, caller: Caller, from: Address, amount: U256) -> Result<bool> {227		let caller = T::CrossAccountId::from_eth(caller);228		let from = T::CrossAccountId::from_eth(from);229		let amount = amount.try_into().map_err(|_| "amount overflow")?;230		let budget = self231			.recorder232			.weight_calls_budget(<StructureWeight<T>>::find_parent());233234		<Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)235			.map_err(dispatch_to_evm::<T>)?;236		Ok(true)237	}238239	/// Burn tokens from account240	/// @dev Function that burns an `amount` of the tokens of a given account,241	/// deducting from the sender's allowance for said account.242	/// @param from The account whose tokens will be burnt.243	/// @param amount The amount that will be burnt.244	#[weight(<SelfWeightOf<T>>::burn_from())]245	fn burn_from_cross(246		&mut self,247		caller: Caller,248		from: CrossAddress,249		amount: U256,250	) -> Result<bool> {251		let caller = T::CrossAccountId::from_eth(caller);252		let from = from.into_sub_cross_account::<T>()?;253		let amount = amount.try_into().map_err(|_| "amount overflow")?;254		let budget = self255			.recorder256			.weight_calls_budget(<StructureWeight<T>>::find_parent());257258		<Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)259			.map_err(dispatch_to_evm::<T>)?;260		Ok(true)261	}262263	/// Mint tokens for multiple accounts.264	/// @param amounts array of pairs of account address and amount265	#[weight(<SelfWeightOf<T>>::create_multiple_items_ex(amounts.len() as u32))]266	fn mint_bulk(&mut self, caller: Caller, amounts: Vec<AmountForAddress>) -> Result<bool> {267		let caller = T::CrossAccountId::from_eth(caller);268		let budget = self269			.recorder270			.weight_calls_budget(<StructureWeight<T>>::find_parent());271		let amounts = amounts272			.into_iter()273			.map(|AmountForAddress { to, amount }| {274				Ok((275					T::CrossAccountId::from_eth(to),276					amount.try_into().map_err(|_| "amount overflow")?,277				))278			})279			.collect::<Result<_>>()?;280281		<Pallet<T>>::create_multiple_items(&self, &caller, amounts, &budget)282			.map_err(dispatch_to_evm::<T>)?;283		Ok(true)284	}285286	#[weight(<SelfWeightOf<T>>::transfer())]287	fn transfer_cross(&mut self, caller: Caller, to: CrossAddress, amount: U256) -> Result<bool> {288		let caller = T::CrossAccountId::from_eth(caller);289		let to = to.into_sub_cross_account::<T>()?;290		let amount = amount.try_into().map_err(|_| "amount overflow")?;291		let budget = self292			.recorder293			.weight_calls_budget(<StructureWeight<T>>::find_parent());294295		<Pallet<T>>::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?;296		Ok(true)297	}298299	#[weight(<SelfWeightOf<T>>::transfer_from())]300	fn transfer_from_cross(301		&mut self,302		caller: Caller,303		from: CrossAddress,304		to: CrossAddress,305		amount: U256,306	) -> Result<bool> {307		let caller = T::CrossAccountId::from_eth(caller);308		let from = from.into_sub_cross_account::<T>()?;309		let to = to.into_sub_cross_account::<T>()?;310		let amount = amount.try_into().map_err(|_| "amount overflow")?;311		let budget = self312			.recorder313			.weight_calls_budget(<StructureWeight<T>>::find_parent());314315		<Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)316			.map_err(dispatch_to_evm::<T>)?;317		Ok(true)318	}319320	/// @notice Returns collection helper contract address321	fn collection_helper_address(&self) -> Result<Address> {322		Ok(T::ContractAddress::get())323	}324}325326#[solidity_interface(327	name = UniqueFungible,328	is(329		ERC20,330		ERC20Mintable,331		ERC20UniqueExtensions,332		Collection(via(common_mut returns CollectionHandle<T>)),333	)334)]335impl<T: Config> FungibleHandle<T> where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]> {}336337generate_stubgen!(gen_impl, UniqueFungibleCall<()>, true);338generate_stubgen!(gen_iface, UniqueFungibleCall<()>, false);339340impl<T: Config> CommonEvmHandler for FungibleHandle<T>341where342	T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,343{344	const CODE: &'static [u8] = include_bytes!("./stubs/UniqueFungible.raw");345346	fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {347		call::<T, UniqueFungibleCall<T>, _, _>(handle, self)348	}349}