1use core::char::{REPLACEMENT_CHARACTER, decode_utf16};2use core::convert::TryInto;3use evm_coder::{ToLog, execution::*, generate_stubgen, solidity_interface, types::*, weight};4use nft_data_structs::CollectionMode;5use pallet_common::erc::CommonEvmHandler;6use sp_core::{H160, U256};7use sp_std::vec::Vec;8use pallet_common::account::CrossAccountId;9use pallet_common::erc::PrecompileOutput;10use pallet_evm_coder_substrate::{call, dispatch_to_evm};1112use crate::{13 Allowance, Balance, Config, FungibleHandle, Pallet, SelfWeightOf, TotalSupply,14 weights::WeightInfo,15};1617#[derive(ToLog)]18pub enum ERC20Events {19 Transfer {20 #[indexed]21 from: address,22 #[indexed]23 to: address,24 value: uint256,25 },26 Approval {27 #[indexed]28 owner: address,29 #[indexed]30 spender: address,31 value: uint256,32 },33}3435#[solidity_interface(name = "ERC20", events(ERC20Events))]36impl<T: Config> FungibleHandle<T> {37 fn name(&self) -> Result<string> {38 Ok(decode_utf16(self.name.iter().copied())39 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))40 .collect::<string>())41 }42 fn symbol(&self) -> Result<string> {43 Ok(string::from_utf8_lossy(&self.token_prefix).into())44 }45 fn total_supply(&self) -> Result<uint256> {46 self.consume_store_reads(1)?;47 Ok(<TotalSupply<T>>::get(self.id).into())48 }4950 fn decimals(&self) -> Result<uint8> {51 Ok(if let CollectionMode::Fungible(decimals) = &self.mode {52 *decimals53 } else {54 unreachable!()55 })56 }57 fn balance_of(&self, owner: address) -> Result<uint256> {58 self.consume_store_reads(1)?;59 let owner = T::CrossAccountId::from_eth(owner);60 let balance = <Balance<T>>::get((self.id, owner));61 Ok(balance.into())62 }63 #[weight(<SelfWeightOf<T>>::transfer())]64 fn transfer(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {65 let caller = T::CrossAccountId::from_eth(caller);66 let to = T::CrossAccountId::from_eth(to);67 let amount = amount.try_into().map_err(|_| "amount overflow")?;6869 <Pallet<T>>::transfer(self, &caller, &to, amount).map_err(|_| "transfer error")?;70 Ok(true)71 }72 #[weight(<SelfWeightOf<T>>::transfer_from())]73 fn transfer_from(74 &mut self,75 caller: caller,76 from: address,77 to: address,78 amount: uint256,79 ) -> Result<bool> {80 let caller = T::CrossAccountId::from_eth(caller);81 let from = T::CrossAccountId::from_eth(from);82 let to = T::CrossAccountId::from_eth(to);83 let amount = amount.try_into().map_err(|_| "amount overflow")?;8485 <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount)86 .map_err(dispatch_to_evm::<T>)?;87 Ok(true)88 }89 #[weight(<SelfWeightOf<T>>::approve())]90 fn approve(&mut self, caller: caller, spender: address, amount: uint256) -> Result<bool> {91 let caller = T::CrossAccountId::from_eth(caller);92 let spender = T::CrossAccountId::from_eth(spender);93 let amount = amount.try_into().map_err(|_| "amount overflow")?;9495 <Pallet<T>>::set_allowance(self, &caller, &spender, amount)96 .map_err(dispatch_to_evm::<T>)?;97 Ok(true)98 }99 fn allowance(&self, owner: address, spender: address) -> Result<uint256> {100 self.consume_store_reads(1)?;101 let owner = T::CrossAccountId::from_eth(owner);102 let spender = T::CrossAccountId::from_eth(spender);103104 Ok(<Allowance<T>>::get((self.id, owner, spender)).into())105 }106}107108#[solidity_interface(name = "ERC20UniqueExtensions")]109impl<T: Config> FungibleHandle<T> {110 #[weight(<SelfWeightOf<T>>::burn_from())]111 fn burn_from(&mut self, caller: caller, from: address, amount: uint256) -> Result<bool> {112 let caller = T::CrossAccountId::from_eth(caller);113 let from = T::CrossAccountId::from_eth(from);114 let amount = amount.try_into().map_err(|_| "amount overflow")?;115116 <Pallet<T>>::burn_from(self, &caller, &from, amount).map_err(dispatch_to_evm::<T>)?;117 Ok(true)118 }119}120121#[solidity_interface(name = "UniqueFungible", is(ERC20))]122impl<T: Config> FungibleHandle<T> {}123124generate_stubgen!(gen_impl, UniqueFungibleCall<()>, true);125generate_stubgen!(gen_iface, UniqueFungibleCall<()>, false);126127impl<T: Config> CommonEvmHandler for FungibleHandle<T> {128 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueFungible.raw");129130 fn call(self, source: &H160, input: &[u8], value: U256) -> Option<PrecompileOutput> {131 call::<T, UniqueFungibleCall<T>, _>(*source, self, value, input)132 }133}