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

difftreelog

source

pallets/refungible/src/erc_token.rs13.8 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//! # Refungible Pallet EVM API for token pieces18//!19//! Provides ERC-20 standart support implementation and EVM API for unique extensions for Refungible Pallet.20//! Method implementations are mostly doing parameter conversion and calling Nonfungible Pallet methods.2122use core::{23	char::{REPLACEMENT_CHARACTER, decode_utf16},24	convert::TryInto,25	ops::Deref,26};27use evm_coder::{abi::AbiType, ToLog, generate_stubgen, solidity_interface, types::*};28use pallet_common::{29	erc::{CommonEvmHandler, PrecompileResult},30	eth::{collection_id_to_address, CrossAddress},31};32use pallet_evm::{account::CrossAccountId, PrecompileHandle};33use pallet_evm_coder_substrate::{34	call, dispatch_to_evm, WithRecorder, frontier_contract,35	execution::{Result, PreDispatch},36};37use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};38use sp_std::vec::Vec;39use sp_core::U256;40use up_data_structs::TokenId;4142use crate::{Allowance, Balance, Config, Pallet, RefungibleHandle, TotalSupply};4344/// Refungible token handle contains information about token's collection and id45///46/// RefungibleTokenHandle doesn't check token's existance upon creation47pub struct RefungibleTokenHandle<T: Config>(pub RefungibleHandle<T>, pub TokenId);4849frontier_contract! {50	macro_rules! RefungibleTokenHandle_result {...}51	impl<T: Config> Contract for RefungibleTokenHandle<T> {...}52}5354#[solidity_interface(name = ERC1633, enum(derive(PreDispatch)), enum_attr(weight))]55impl<T: Config> RefungibleTokenHandle<T> {56	fn parent_token(&self) -> Address {57		collection_id_to_address(self.id)58	}5960	fn parent_token_id(&self) -> U256 {61		self.1.into()62	}63}6465#[derive(ToLog)]66pub enum ERC20Events {67	/// @dev This event is emitted when the amount of tokens (value) is sent68	/// from the from address to the to address. In the case of minting new69	/// tokens, the transfer is usually from the 0 address while in the case70	/// of burning tokens the transfer is to 0.71	Transfer {72		#[indexed]73		from: Address,74		#[indexed]75		to: Address,76		value: U256,77	},78	/// @dev This event is emitted when the amount of tokens (value) is approved79	/// by the owner to be used by the spender.80	Approval {81		#[indexed]82		owner: Address,83		#[indexed]84		spender: Address,85		value: U256,86	},87}8889/// @title Standard ERC20 token90///91/// @dev Implementation of the basic standard token.92/// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md93#[solidity_interface(name = ERC20, events(ERC20Events), enum(derive(PreDispatch)), enum_attr(weight))]94impl<T: Config> RefungibleTokenHandle<T> {95	/// @return the name of the token.96	fn name(&self) -> String {97		decode_utf16(self.name.iter().copied())98			.map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))99			.collect::<String>()100	}101102	/// @return the symbol of the token.103	fn symbol(&self) -> String {104		String::from_utf8_lossy(&self.token_prefix).into()105	}106107	/// @dev Total number of tokens in existence108	fn total_supply(&self) -> Result<U256> {109		self.consume_store_reads(1)?;110		Ok(<TotalSupply<T>>::get((self.id, self.1)).into())111	}112113	/// @dev Not supported114	fn decimals(&self) -> Result<u8> {115		// Decimals aren't supported for refungible tokens116		Ok(0)117	}118119	/// @dev Gets the balance of the specified address.120	/// @param owner The address to query the balance of.121	/// @return An uint256 representing the amount owned by the passed address.122	fn balance_of(&self, owner: Address) -> Result<U256> {123		self.consume_store_reads(1)?;124		let owner = T::CrossAccountId::from_eth(owner);125		let balance = <Balance<T>>::get((self.id, self.1, owner));126		Ok(balance.into())127	}128129	/// @dev Transfer token for a specified address130	/// @param to The address to transfer to.131	/// @param amount The amount to be transferred.132	#[weight(<CommonWeights<T>>::transfer())]133	fn transfer(&mut self, caller: Caller, to: Address, amount: U256) -> Result<bool> {134		let caller = T::CrossAccountId::from_eth(caller);135		let to = T::CrossAccountId::from_eth(to);136		let amount = amount.try_into().map_err(|_| "amount overflow")?;137		let budget = self138			.recorder139			.weight_calls_budget(<StructureWeight<T>>::find_parent());140141		<Pallet<T>>::transfer(self, &caller, &to, self.1, amount, &budget)142			.map_err(dispatch_to_evm::<T>)?;143		Ok(true)144	}145146	/// @dev Transfer tokens from one address to another147	/// @param from address The address which you want to send tokens from148	/// @param to address The address which you want to transfer to149	/// @param amount uint256 the amount of tokens to be transferred150	#[weight(<CommonWeights<T>>::transfer_from())]151	fn transfer_from(152		&mut self,153		caller: Caller,154		from: Address,155		to: Address,156		amount: U256,157	) -> Result<bool> {158		let caller = T::CrossAccountId::from_eth(caller);159		let from = T::CrossAccountId::from_eth(from);160		let to = T::CrossAccountId::from_eth(to);161		let amount = amount.try_into().map_err(|_| "amount overflow")?;162		let budget = self163			.recorder164			.weight_calls_budget(<StructureWeight<T>>::find_parent());165166		<Pallet<T>>::transfer_from(self, &caller, &from, &to, self.1, amount, &budget)167			.map_err(dispatch_to_evm::<T>)?;168		Ok(true)169	}170171	/// @dev Approve the passed address to spend the specified amount of tokens on behalf of `msg.sender`.172	/// Beware that changing an allowance with this method brings the risk that someone may use both the old173	/// and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this174	/// race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:175	/// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729176	/// @param spender The address which will spend the funds.177	/// @param amount The amount of tokens to be spent.178	#[weight(<SelfWeightOf<T>>::approve())]179	fn approve(&mut self, caller: Caller, spender: Address, amount: U256) -> Result<bool> {180		let caller = T::CrossAccountId::from_eth(caller);181		let spender = T::CrossAccountId::from_eth(spender);182		let amount = amount.try_into().map_err(|_| "amount overflow")?;183184		<Pallet<T>>::set_allowance(self, &caller, &spender, self.1, amount)185			.map_err(dispatch_to_evm::<T>)?;186		Ok(true)187	}188189	/// @dev Function to check the amount of tokens that an owner allowed to a spender.190	/// @param owner address The address which owns the funds.191	/// @param spender address The address which will spend the funds.192	/// @return A uint256 specifying the amount of tokens still available for the spender.193	fn allowance(&self, owner: Address, spender: Address) -> Result<U256> {194		self.consume_store_reads(1)?;195		let owner = T::CrossAccountId::from_eth(owner);196		let spender = T::CrossAccountId::from_eth(spender);197198		Ok(<Allowance<T>>::get((self.id, self.1, owner, spender)).into())199	}200}201202#[solidity_interface(name = ERC20UniqueExtensions, enum(derive(PreDispatch)), enum_attr(weight))]203impl<T: Config> RefungibleTokenHandle<T>204where205	T::AccountId: From<[u8; 32]>,206{207	/// @dev Function to check the amount of tokens that an owner allowed to a spender.208	/// @param owner crossAddress The address which owns the funds.209	/// @param spender crossAddress The address which will spend the funds.210	/// @return A uint256 specifying the amount of tokens still available for the spender.211	fn allowance_cross(&self, owner: CrossAddress, spender: CrossAddress) -> Result<U256> {212		let owner = owner.into_sub_cross_account::<T>()?;213		let spender = spender.into_sub_cross_account::<T>()?;214215		Ok(<Allowance<T>>::get((self.id, self.1, owner, spender)).into())216	}217218	/// @dev Function that burns an amount of the token of a given account,219	/// deducting from the sender's allowance for said account.220	/// @param from The account whose tokens will be burnt.221	/// @param amount The amount that will be burnt.222	#[weight(<SelfWeightOf<T>>::burn_from())]223	#[solidity(hide)]224	fn burn_from(&mut self, caller: Caller, from: Address, amount: U256) -> Result<bool> {225		let caller = T::CrossAccountId::from_eth(caller);226		let from = T::CrossAccountId::from_eth(from);227		let amount = amount.try_into().map_err(|_| "amount overflow")?;228		let budget = self229			.recorder230			.weight_calls_budget(<StructureWeight<T>>::find_parent());231232		<Pallet<T>>::burn_from(self, &caller, &from, self.1, amount, &budget)233			.map_err(dispatch_to_evm::<T>)?;234		Ok(true)235	}236237	/// @dev Function that burns an amount of the token of a given account,238	/// deducting from the sender's allowance for said account.239	/// @param from The account whose tokens will be burnt.240	/// @param amount The amount that will be burnt.241	#[weight(<SelfWeightOf<T>>::burn_from())]242	fn burn_from_cross(243		&mut self,244		caller: Caller,245		from: CrossAddress,246		amount: U256,247	) -> Result<bool> {248		let caller = T::CrossAccountId::from_eth(caller);249		let from = from.into_sub_cross_account::<T>()?;250		let amount = amount.try_into().map_err(|_| "amount overflow")?;251		let budget = self252			.recorder253			.weight_calls_budget(<StructureWeight<T>>::find_parent());254255		<Pallet<T>>::burn_from(self, &caller, &from, self.1, amount, &budget)256			.map_err(dispatch_to_evm::<T>)?;257		Ok(true)258	}259260	/// @dev Approve the passed address to spend the specified amount of tokens on behalf of `msg.sender`.261	/// Beware that changing an allowance with this method brings the risk that someone may use both the old262	/// and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this263	/// race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:264	/// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729265	/// @param spender The crossaccount which will spend the funds.266	/// @param amount The amount of tokens to be spent.267	#[weight(<SelfWeightOf<T>>::approve())]268	fn approve_cross(269		&mut self,270		caller: Caller,271		spender: CrossAddress,272		amount: U256,273	) -> Result<bool> {274		let caller = T::CrossAccountId::from_eth(caller);275		let spender = spender.into_sub_cross_account::<T>()?;276		let amount = amount.try_into().map_err(|_| "amount overflow")?;277278		<Pallet<T>>::set_allowance(self, &caller, &spender, self.1, amount)279			.map_err(dispatch_to_evm::<T>)?;280		Ok(true)281	}282	/// @dev Function that changes total amount of the tokens.283	///  Throws if `msg.sender` doesn't owns all of the tokens.284	/// @param amount New total amount of the tokens.285	#[weight(<SelfWeightOf<T>>::repartition_item())]286	fn repartition(&mut self, caller: Caller, amount: U256) -> Result<bool> {287		let caller = T::CrossAccountId::from_eth(caller);288		let amount = amount.try_into().map_err(|_| "amount overflow")?;289290		<Pallet<T>>::repartition(self, &caller, self.1, amount).map_err(dispatch_to_evm::<T>)?;291		Ok(true)292	}293294	/// @dev Transfer token for a specified address295	/// @param to The crossaccount to transfer to.296	/// @param amount The amount to be transferred.297	#[weight(<CommonWeights<T>>::transfer())]298	fn transfer_cross(&mut self, caller: Caller, to: CrossAddress, amount: U256) -> Result<bool> {299		let caller = T::CrossAccountId::from_eth(caller);300		let to = to.into_sub_cross_account::<T>()?;301		let amount = amount.try_into().map_err(|_| "amount overflow")?;302		let budget = self303			.recorder304			.weight_calls_budget(<StructureWeight<T>>::find_parent());305306		<Pallet<T>>::transfer(self, &caller, &to, self.1, amount, &budget)307			.map_err(dispatch_to_evm::<T>)?;308		Ok(true)309	}310311	/// @dev Transfer tokens from one address to another312	/// @param from The address which you want to send tokens from313	/// @param to The address which you want to transfer to314	/// @param amount the amount of tokens to be transferred315	#[weight(<CommonWeights<T>>::transfer_from())]316	fn transfer_from_cross(317		&mut self,318		caller: Caller,319		from: CrossAddress,320		to: CrossAddress,321		amount: U256,322	) -> Result<bool> {323		let caller = T::CrossAccountId::from_eth(caller);324		let from = from.into_sub_cross_account::<T>()?;325		let to = to.into_sub_cross_account::<T>()?;326		let amount = amount.try_into().map_err(|_| "amount overflow")?;327		let budget = self328			.recorder329			.weight_calls_budget(<StructureWeight<T>>::find_parent());330331		<Pallet<T>>::transfer_from(self, &caller, &from, &to, self.1, amount, &budget)332			.map_err(dispatch_to_evm::<T>)?;333		Ok(true)334	}335}336337impl<T: Config> RefungibleTokenHandle<T> {338	pub fn into_inner(self) -> RefungibleHandle<T> {339		self.0340	}341	pub fn common_mut(&mut self) -> &mut RefungibleHandle<T> {342		&mut self.0343	}344}345346impl<T: Config> WithRecorder<T> for RefungibleTokenHandle<T> {347	fn recorder(&self) -> &pallet_evm_coder_substrate::SubstrateRecorder<T> {348		self.0.recorder()349	}350	fn into_recorder(self) -> pallet_evm_coder_substrate::SubstrateRecorder<T> {351		self.0.into_recorder()352	}353}354355impl<T: Config> Deref for RefungibleTokenHandle<T> {356	type Target = RefungibleHandle<T>;357358	fn deref(&self) -> &Self::Target {359		&self.0360	}361}362363#[solidity_interface(364	name = UniqueRefungibleToken,365	is(ERC20, ERC20UniqueExtensions, ERC1633),366	enum(derive(PreDispatch)),367)]368impl<T: Config> RefungibleTokenHandle<T> where T::AccountId: From<[u8; 32]> {}369370generate_stubgen!(gen_impl, UniqueRefungibleTokenCall<()>, true);371generate_stubgen!(gen_iface, UniqueRefungibleTokenCall<()>, false);372373impl<T: Config> CommonEvmHandler for RefungibleTokenHandle<T>374where375	T::AccountId: From<[u8; 32]>,376{377	const CODE: &'static [u8] = include_bytes!("./stubs/UniqueRefungibleToken.raw");378379	fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {380		call::<T, UniqueRefungibleTokenCall<T>, _, _>(handle, self)381	}382}