1234567891011121314151617use core::char::{REPLACEMENT_CHARACTER, decode_utf16};18use core::convert::TryInto;19use evm_coder::{ToLog, execution::*, generate_stubgen, solidity_interface, types::*, weight};20use up_data_structs::CollectionMode;21use pallet_common::erc::{CommonEvmHandler, PrecompileResult};22use sp_core::{H160, U256};23use sp_std::vec::Vec;24use pallet_evm::account::CrossAccountId;25use pallet_evm_coder_substrate::{call, dispatch_to_evm};26use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};27use pallet_common::{CollectionHandle, erc::CollectionCall};2829use crate::{30 Allowance, Balance, Config, FungibleHandle, Pallet, SelfWeightOf, TotalSupply,31 weights::WeightInfo,32};3334#[derive(ToLog)]35pub enum ERC20Events {36 Transfer {37 #[indexed]38 from: address,39 #[indexed]40 to: address,41 value: uint256,42 },43 Approval {44 #[indexed]45 owner: address,46 #[indexed]47 spender: address,48 value: uint256,49 },50}5152#[solidity_interface(name = "ERC20", events(ERC20Events))]53impl<T: Config> FungibleHandle<T> {54 fn name(&self) -> Result<string> {55 Ok(decode_utf16(self.name.iter().copied())56 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))57 .collect::<string>())58 }59 fn symbol(&self) -> Result<string> {60 Ok(string::from_utf8_lossy(&self.token_prefix).into())61 }62 fn total_supply(&self) -> Result<uint256> {63 self.consume_store_reads(1)?;64 Ok(<TotalSupply<T>>::get(self.id).into())65 }6667 fn decimals(&self) -> Result<uint8> {68 Ok(if let CollectionMode::Fungible(decimals) = &self.mode {69 *decimals70 } else {71 unreachable!()72 })73 }74 fn balance_of(&self, owner: address) -> Result<uint256> {75 self.consume_store_reads(1)?;76 let owner = T::CrossAccountId::from_eth(owner);77 let balance = <Balance<T>>::get((self.id, owner));78 Ok(balance.into())79 }80 #[weight(<SelfWeightOf<T>>::transfer())]81 fn transfer(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {82 let caller = T::CrossAccountId::from_eth(caller);83 let to = T::CrossAccountId::from_eth(to);84 let amount = amount.try_into().map_err(|_| "amount overflow")?;85 let budget = self86 .recorder87 .weight_calls_budget(<StructureWeight<T>>::find_parent());8889 <Pallet<T>>::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?;90 Ok(true)91 }92 #[weight(<SelfWeightOf<T>>::transfer_from())]93 fn transfer_from(94 &mut self,95 caller: caller,96 from: address,97 to: address,98 amount: uint256,99 ) -> Result<bool> {100 let caller = T::CrossAccountId::from_eth(caller);101 let from = T::CrossAccountId::from_eth(from);102 let to = T::CrossAccountId::from_eth(to);103 let amount = amount.try_into().map_err(|_| "amount overflow")?;104 let budget = self105 .recorder106 .weight_calls_budget(<StructureWeight<T>>::find_parent());107108 <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)109 .map_err(dispatch_to_evm::<T>)?;110 Ok(true)111 }112 #[weight(<SelfWeightOf<T>>::approve())]113 fn approve(&mut self, caller: caller, spender: address, amount: uint256) -> Result<bool> {114 let caller = T::CrossAccountId::from_eth(caller);115 let spender = T::CrossAccountId::from_eth(spender);116 let amount = amount.try_into().map_err(|_| "amount overflow")?;117118 <Pallet<T>>::set_allowance(self, &caller, &spender, amount)119 .map_err(dispatch_to_evm::<T>)?;120 Ok(true)121 }122 fn allowance(&self, owner: address, spender: address) -> Result<uint256> {123 self.consume_store_reads(1)?;124 let owner = T::CrossAccountId::from_eth(owner);125 let spender = T::CrossAccountId::from_eth(spender);126127 Ok(<Allowance<T>>::get((self.id, owner, spender)).into())128 }129}130131#[solidity_interface(name = "ERC20UniqueExtensions")]132impl<T: Config> FungibleHandle<T> {133 #[weight(<SelfWeightOf<T>>::burn_from())]134 fn burn_from(&mut self, caller: caller, from: address, amount: uint256) -> Result<bool> {135 let caller = T::CrossAccountId::from_eth(caller);136 let from = T::CrossAccountId::from_eth(from);137 let amount = amount.try_into().map_err(|_| "amount overflow")?;138 let budget = self139 .recorder140 .weight_calls_budget(<StructureWeight<T>>::find_parent());141142 <Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)143 .map_err(dispatch_to_evm::<T>)?;144 Ok(true)145 }146}147148#[solidity_interface(149 name = "UniqueFungible",150 is(151 ERC20,152 ERC20UniqueExtensions,153 via("CollectionHandle<T>", common_mut, Collection)154 )155)]156impl<T: Config> FungibleHandle<T> {}157158generate_stubgen!(gen_impl, UniqueFungibleCall<()>, true);159generate_stubgen!(gen_iface, UniqueFungibleCall<()>, false);160161impl<T: Config> CommonEvmHandler for FungibleHandle<T> {162 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueFungible.raw");163164 fn call(self, source: &H160, input: &[u8], value: U256) -> Option<PrecompileResult> {165 call::<T, UniqueFungibleCall<T>, _>(*source, self, value, input)166 }167}