git.delta.rocks / unique-network / refs/commits / 90ad566cc7e8

difftreelog

source

pallets/fungible/src/erc.rs11.9 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,36};37use pallet_structure::{weights::WeightInfo as _, SelfWeightOf as StructureWeight};38use sp_core::{Get, U256};39use sp_std::vec::Vec;40use up_data_structs::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}7576#[solidity_interface(name = ERC20, events(ERC20Events), enum(derive(PreDispatch)), enum_attr(weight), expect_selector = 0x942e8b22)]77impl<T: Config> FungibleHandle<T> {78	fn name(&self) -> Result<String> {79		Ok(decode_utf16(self.name.iter().copied())80			.map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))81			.collect::<String>())82	}83	fn symbol(&self) -> Result<String> {84		Ok(String::from_utf8_lossy(&self.token_prefix).into())85	}86	fn total_supply(&self) -> Result<U256> {87		self.consume_store_reads(1)?;88		Ok(<TotalSupply<T>>::get(self.id).into())89	}9091	fn decimals(&self) -> Result<u8> {92		Ok(if let CollectionMode::Fungible(decimals) = &self.mode {93			*decimals94		} else {95			unreachable!()96		})97	}98	fn balance_of(&self, owner: Address) -> Result<U256> {99		self.consume_store_reads(1)?;100		let owner = T::CrossAccountId::from_eth(owner);101		let balance = <Balance<T>>::get((self.id, owner));102		Ok(balance.into())103	}104	#[weight(<CommonWeights<T>>::transfer())]105	fn transfer(&mut self, caller: Caller, to: Address, amount: U256) -> Result<bool> {106		let caller = T::CrossAccountId::from_eth(caller);107		let to = T::CrossAccountId::from_eth(to);108		let amount = amount.try_into().map_err(|_| "amount overflow")?;109		let budget = self110			.recorder111			.weight_calls_budget(<StructureWeight<T>>::find_parent());112113		<Pallet<T>>::transfer(self, &caller, &to, amount, &budget)114			.map_err(|e| dispatch_to_evm::<T>(e.error))?;115		Ok(true)116	}117118	#[weight(<CommonWeights<T>>::transfer_from())]119	fn transfer_from(120		&mut self,121		caller: Caller,122		from: Address,123		to: Address,124		amount: U256,125	) -> Result<bool> {126		let caller = T::CrossAccountId::from_eth(caller);127		let from = T::CrossAccountId::from_eth(from);128		let to = T::CrossAccountId::from_eth(to);129		let amount = amount.try_into().map_err(|_| "amount overflow")?;130		let budget = self131			.recorder132			.weight_calls_budget(<StructureWeight<T>>::find_parent());133134		<Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)135			.map_err(|e| dispatch_to_evm::<T>(e.error))?;136		Ok(true)137	}138	#[weight(<SelfWeightOf<T>>::approve())]139	fn approve(&mut self, caller: Caller, spender: Address, amount: U256) -> Result<bool> {140		let caller = T::CrossAccountId::from_eth(caller);141		let spender = T::CrossAccountId::from_eth(spender);142		let amount = amount.try_into().map_err(|_| "amount overflow")?;143144		<Pallet<T>>::set_allowance(self, &caller, &spender, amount)145			.map_err(dispatch_to_evm::<T>)?;146		Ok(true)147	}148	fn allowance(&self, owner: Address, spender: Address) -> Result<U256> {149		self.consume_store_reads(1)?;150		let owner = T::CrossAccountId::from_eth(owner);151		let spender = T::CrossAccountId::from_eth(spender);152153		Ok(<Allowance<T>>::get((self.id, owner, spender)).into())154	}155}156157#[solidity_interface(name = ERC20Mintable, enum(derive(PreDispatch)), enum_attr(weight))]158impl<T: Config> FungibleHandle<T> {159	/// Mint tokens for `to` account.160	/// @param to account that will receive minted tokens161	/// @param amount amount of tokens to mint162	#[weight(<SelfWeightOf<T>>::create_item())]163	fn mint(&mut self, caller: Caller, to: Address, amount: U256) -> Result<bool> {164		let caller = T::CrossAccountId::from_eth(caller);165		let to = T::CrossAccountId::from_eth(to);166		let amount = amount.try_into().map_err(|_| "amount overflow")?;167		let budget = self168			.recorder169			.weight_calls_budget(<StructureWeight<T>>::find_parent());170		<Pallet<T>>::create_item(self, &caller, (to, amount), &budget)171			.map_err(dispatch_to_evm::<T>)?;172		Ok(true)173	}174}175176#[solidity_interface(name = ERC20UniqueExtensions, enum(derive(PreDispatch)), enum_attr(weight))]177impl<T: Config> FungibleHandle<T>178where179	T::AccountId: From<[u8; 32]>,180{181	/// @dev Function to check the amount of tokens that an owner allowed to a spender.182	/// @param owner crossAddress The address which owns the funds.183	/// @param spender crossAddress The address which will spend the funds.184	/// @return A uint256 specifying the amount of tokens still available for the spender.185	fn allowance_cross(&self, owner: CrossAddress, spender: CrossAddress) -> Result<U256> {186		let owner = owner.into_sub_cross_account::<T>()?;187		let spender = spender.into_sub_cross_account::<T>()?;188189		Ok(<Allowance<T>>::get((self.id, owner, spender)).into())190	}191192	/// @notice A description for the collection.193	fn description(&self) -> String {194		decode_utf16(self.description.iter().copied())195			.map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))196			.collect::<String>()197	}198199	#[weight(<SelfWeightOf<T>>::create_item())]200	fn mint_cross(&mut self, caller: Caller, to: CrossAddress, amount: U256) -> Result<bool> {201		let caller = T::CrossAccountId::from_eth(caller);202		let to = to.into_sub_cross_account::<T>()?;203		let amount = amount.try_into().map_err(|_| "amount overflow")?;204		let budget = self205			.recorder206			.weight_calls_budget(<StructureWeight<T>>::find_parent());207		<Pallet<T>>::create_item(self, &caller, (to, amount), &budget)208			.map_err(dispatch_to_evm::<T>)?;209		Ok(true)210	}211212	#[weight(<SelfWeightOf<T>>::approve())]213	fn approve_cross(214		&mut self,215		caller: Caller,216		spender: CrossAddress,217		amount: U256,218	) -> Result<bool> {219		let caller = T::CrossAccountId::from_eth(caller);220		let spender = spender.into_sub_cross_account::<T>()?;221		let amount = amount.try_into().map_err(|_| "amount overflow")?;222223		<Pallet<T>>::set_allowance(self, &caller, &spender, amount)224			.map_err(dispatch_to_evm::<T>)?;225		Ok(true)226	}227228	/// Burn tokens from account229	/// @dev Function that burns an `amount` of the tokens of a given account,230	/// deducting from the sender's allowance for said account.231	/// @param from The account whose tokens will be burnt.232	/// @param amount The amount that will be burnt.233	#[solidity(hide)]234	#[weight(<SelfWeightOf<T>>::burn_from())]235	fn burn_from(&mut self, caller: Caller, from: Address, amount: U256) -> Result<bool> {236		let caller = T::CrossAccountId::from_eth(caller);237		let from = T::CrossAccountId::from_eth(from);238		let amount = amount.try_into().map_err(|_| "amount overflow")?;239		let budget = self240			.recorder241			.weight_calls_budget(<StructureWeight<T>>::find_parent());242243		<Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)244			.map_err(dispatch_to_evm::<T>)?;245		Ok(true)246	}247248	/// Burn tokens from account249	/// @dev Function that burns an `amount` of the tokens of a given account,250	/// deducting from the sender's allowance for said account.251	/// @param from The account whose tokens will be burnt.252	/// @param amount The amount that will be burnt.253	#[weight(<SelfWeightOf<T>>::burn_from())]254	fn burn_from_cross(255		&mut self,256		caller: Caller,257		from: CrossAddress,258		amount: U256,259	) -> Result<bool> {260		let caller = T::CrossAccountId::from_eth(caller);261		let from = from.into_sub_cross_account::<T>()?;262		let amount = amount.try_into().map_err(|_| "amount overflow")?;263		let budget = self264			.recorder265			.weight_calls_budget(<StructureWeight<T>>::find_parent());266267		<Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)268			.map_err(dispatch_to_evm::<T>)?;269		Ok(true)270	}271272	/// Mint tokens for multiple accounts.273	/// @param amounts array of pairs of account address and amount274	#[weight(<SelfWeightOf<T>>::create_multiple_items_ex(amounts.len() as u32))]275	fn mint_bulk(&mut self, caller: Caller, amounts: Vec<AmountForAddress>) -> Result<bool> {276		let caller = T::CrossAccountId::from_eth(caller);277		let budget = self278			.recorder279			.weight_calls_budget(<StructureWeight<T>>::find_parent());280		let amounts = amounts281			.into_iter()282			.map(|AmountForAddress { to, amount }| {283				Ok((284					T::CrossAccountId::from_eth(to),285					amount.try_into().map_err(|_| "amount overflow")?,286				))287			})288			.collect::<Result<_>>()?;289290		<Pallet<T>>::create_multiple_items(self, &caller, amounts, &budget)291			.map_err(dispatch_to_evm::<T>)?;292		Ok(true)293	}294295	#[weight(<CommonWeights<T>>::transfer())]296	fn transfer_cross(&mut self, caller: Caller, to: CrossAddress, amount: U256) -> Result<bool> {297		let caller = T::CrossAccountId::from_eth(caller);298		let to = to.into_sub_cross_account::<T>()?;299		let amount = amount.try_into().map_err(|_| "amount overflow")?;300		let budget = self301			.recorder302			.weight_calls_budget(<StructureWeight<T>>::find_parent());303304		<Pallet<T>>::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?;305		Ok(true)306	}307308	#[weight(<CommonWeights<T>>::transfer_from())]309	fn transfer_from_cross(310		&mut self,311		caller: Caller,312		from: CrossAddress,313		to: CrossAddress,314		amount: U256,315	) -> Result<bool> {316		let caller = T::CrossAccountId::from_eth(caller);317		let from = from.into_sub_cross_account::<T>()?;318		let to = to.into_sub_cross_account::<T>()?;319		let amount = amount.try_into().map_err(|_| "amount overflow")?;320		let budget = self321			.recorder322			.weight_calls_budget(<StructureWeight<T>>::find_parent());323324		<Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)325			.map_err(|e| dispatch_to_evm::<T>(e.error))?;326		Ok(true)327	}328329	/// @notice Returns collection helper contract address330	fn collection_helper_address(&self) -> Result<Address> {331		Ok(T::ContractAddress::get())332	}333334	/// @notice Balance of account335	/// @param owner An cross address for whom to query the balance336	/// @return The number of fingibles owned by `owner`, possibly zero337	fn balance_of_cross(&self, owner: CrossAddress) -> Result<U256> {338		self.consume_store_reads(1)?;339		let balance = <Balance<T>>::get((self.id, owner.into_sub_cross_account::<T>()?));340		Ok(balance.into())341	}342}343344#[solidity_interface(345	name = UniqueFungible,346	is(347		ERC20,348		ERC20Mintable,349		ERC20UniqueExtensions,350		Collection(via(common_mut returns CollectionHandle<T>)),351	),352	enum(derive(PreDispatch))353)]354impl<T: Config> FungibleHandle<T> where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]> {}355356generate_stubgen!(gen_impl, UniqueFungibleCall<()>, true);357generate_stubgen!(gen_iface, UniqueFungibleCall<()>, false);358359impl<T: Config> CommonEvmHandler for FungibleHandle<T>360where361	T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,362{363	const CODE: &'static [u8] = include_bytes!("./stubs/UniqueFungible.raw");364365	fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {366		call::<T, UniqueFungibleCall<T>, _, _>(handle, self)367	}368}