12345678910111213141516171819use core::char::{REPLACEMENT_CHARACTER, decode_utf16};20use core::convert::TryInto;21use evm_coder::{ToLog, execution::*, generate_stubgen, solidity_interface, types::*, weight};22use pallet_common::eth::convert_tuple_to_cross_account;23use up_data_structs::CollectionMode;24use pallet_common::erc::{CommonEvmHandler, PrecompileResult};25use sp_std::vec::Vec;26use pallet_evm::{account::CrossAccountId, PrecompileHandle};27use pallet_evm_coder_substrate::{call, dispatch_to_evm};28use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};29use pallet_common::{CollectionHandle, erc::CollectionCall};3031use crate::{32 Allowance, Balance, Config, FungibleHandle, Pallet, SelfWeightOf, TotalSupply,33 weights::WeightInfo,34};3536#[derive(ToLog)]37pub enum ERC20Events {38 Transfer {39 #[indexed]40 from: address,41 #[indexed]42 to: address,43 value: uint256,44 },45 Approval {46 #[indexed]47 owner: address,48 #[indexed]49 spender: address,50 value: uint256,51 },52}5354#[solidity_interface(name = ERC20, events(ERC20Events))]55impl<T: Config> FungibleHandle<T> {56 fn name(&self) -> Result<string> {57 Ok(decode_utf16(self.name.iter().copied())58 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))59 .collect::<string>())60 }61 fn symbol(&self) -> Result<string> {62 Ok(string::from_utf8_lossy(&self.token_prefix).into())63 }64 fn total_supply(&self) -> Result<uint256> {65 self.consume_store_reads(1)?;66 Ok(<TotalSupply<T>>::get(self.id).into())67 }6869 fn decimals(&self) -> Result<uint8> {70 Ok(if let CollectionMode::Fungible(decimals) = &self.mode {71 *decimals72 } else {73 unreachable!()74 })75 }76 fn balance_of(&self, owner: address) -> Result<uint256> {77 self.consume_store_reads(1)?;78 let owner = T::CrossAccountId::from_eth(owner);79 let balance = <Balance<T>>::get((self.id, owner));80 Ok(balance.into())81 }82 #[weight(<SelfWeightOf<T>>::transfer())]83 fn transfer(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {84 let caller = T::CrossAccountId::from_eth(caller);85 let to = T::CrossAccountId::from_eth(to);86 let amount = amount.try_into().map_err(|_| "amount overflow")?;87 let budget = self88 .recorder89 .weight_calls_budget(<StructureWeight<T>>::find_parent());9091 <Pallet<T>>::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?;92 Ok(true)93 }94 #[weight(<SelfWeightOf<T>>::transfer_from())]95 fn transfer_from(96 &mut self,97 caller: caller,98 from: address,99 to: address,100 amount: uint256,101 ) -> Result<bool> {102 let caller = T::CrossAccountId::from_eth(caller);103 let from = T::CrossAccountId::from_eth(from);104 let to = T::CrossAccountId::from_eth(to);105 let amount = amount.try_into().map_err(|_| "amount overflow")?;106 let budget = self107 .recorder108 .weight_calls_budget(<StructureWeight<T>>::find_parent());109110 <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)111 .map_err(dispatch_to_evm::<T>)?;112 Ok(true)113 }114 #[weight(<SelfWeightOf<T>>::approve())]115 fn approve(&mut self, caller: caller, spender: address, amount: uint256) -> Result<bool> {116 let caller = T::CrossAccountId::from_eth(caller);117 let spender = T::CrossAccountId::from_eth(spender);118 let amount = amount.try_into().map_err(|_| "amount overflow")?;119120 <Pallet<T>>::set_allowance(self, &caller, &spender, amount)121 .map_err(dispatch_to_evm::<T>)?;122 Ok(true)123 }124 fn allowance(&self, owner: address, spender: address) -> Result<uint256> {125 self.consume_store_reads(1)?;126 let owner = T::CrossAccountId::from_eth(owner);127 let spender = T::CrossAccountId::from_eth(spender);128129 Ok(<Allowance<T>>::get((self.id, owner, spender)).into())130 }131}132133#[solidity_interface(name = ERC20Mintable)]134impl<T: Config> FungibleHandle<T> {135 136 137 138 #[weight(<SelfWeightOf<T>>::create_item())]139 fn mint(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {140 let caller = T::CrossAccountId::from_eth(caller);141 let to = T::CrossAccountId::from_eth(to);142 let amount = amount.try_into().map_err(|_| "amount overflow")?;143 let budget = self144 .recorder145 .weight_calls_budget(<StructureWeight<T>>::find_parent());146 <Pallet<T>>::create_item(&self, &caller, (to, amount), &budget)147 .map_err(dispatch_to_evm::<T>)?;148 Ok(true)149 }150}151152#[solidity_interface(name = ERC20UniqueExtensions)]153impl<T: Config> FungibleHandle<T>154where155 T::AccountId: From<[u8; 32]>,156{157 #[weight(<SelfWeightOf<T>>::approve())]158 fn approve_cross(159 &mut self,160 caller: caller,161 spender: (address, uint256),162 amount: uint256,163 ) -> Result<bool> {164 let caller = T::CrossAccountId::from_eth(caller);165 let spender = convert_tuple_to_cross_account::<T>(spender)?;166 let amount = amount.try_into().map_err(|_| "amount overflow")?;167168 <Pallet<T>>::set_allowance(self, &caller, &spender, amount)169 .map_err(dispatch_to_evm::<T>)?;170 Ok(true)171 }172173 174 175 176 177 178 #[weight(<SelfWeightOf<T>>::burn_from())]179 fn burn_from(&mut self, caller: caller, from: address, amount: uint256) -> Result<bool> {180 let caller = T::CrossAccountId::from_eth(caller);181 let from = T::CrossAccountId::from_eth(from);182 let amount = amount.try_into().map_err(|_| "amount overflow")?;183 let budget = self184 .recorder185 .weight_calls_budget(<StructureWeight<T>>::find_parent());186187 <Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)188 .map_err(dispatch_to_evm::<T>)?;189 Ok(true)190 }191192 193 194 195 196 197 #[weight(<SelfWeightOf<T>>::burn_from())]198 fn burn_from_cross(199 &mut self,200 caller: caller,201 from: (address, uint256),202 amount: uint256,203 ) -> Result<bool> {204 let caller = T::CrossAccountId::from_eth(caller);205 let from = convert_tuple_to_cross_account::<T>(from)?;206 let amount = amount.try_into().map_err(|_| "amount overflow")?;207 let budget = self208 .recorder209 .weight_calls_budget(<StructureWeight<T>>::find_parent());210211 <Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)212 .map_err(dispatch_to_evm::<T>)?;213 Ok(true)214 }215216 217 218 #[weight(<SelfWeightOf<T>>::create_multiple_items_ex(amounts.len() as u32))]219 fn mint_bulk(&mut self, caller: caller, amounts: Vec<(address, uint256)>) -> Result<bool> {220 let caller = T::CrossAccountId::from_eth(caller);221 let budget = self222 .recorder223 .weight_calls_budget(<StructureWeight<T>>::find_parent());224 let amounts = amounts225 .into_iter()226 .map(|(to, amount)| {227 Ok((228 T::CrossAccountId::from_eth(to),229 amount.try_into().map_err(|_| "amount overflow")?,230 ))231 })232 .collect::<Result<_>>()?;233234 <Pallet<T>>::create_multiple_items(&self, &caller, amounts, &budget)235 .map_err(dispatch_to_evm::<T>)?;236 Ok(true)237 }238239 #[weight(<SelfWeightOf<T>>::transfer_from())]240 fn transfer_from_cross(241 &mut self,242 caller: caller,243 from: (address, uint256),244 to: (address, uint256),245 amount: uint256,246 ) -> Result<bool> {247 let caller = T::CrossAccountId::from_eth(caller);248 let from = convert_tuple_to_cross_account::<T>(from)?;249 let to = convert_tuple_to_cross_account::<T>(to)?;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>>::transfer_from(self, &caller, &from, &to, amount, &budget)256 .map_err(dispatch_to_evm::<T>)?;257 Ok(true)258 }259}260261#[solidity_interface(262 name = UniqueFungible,263 is(264 ERC20,265 ERC20Mintable,266 ERC20UniqueExtensions,267 Collection(via(common_mut returns CollectionHandle<T>)),268 )269)]270impl<T: Config> FungibleHandle<T> where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]> {}271272generate_stubgen!(gen_impl, UniqueFungibleCall<()>, true);273generate_stubgen!(gen_iface, UniqueFungibleCall<()>, false);274275impl<T: Config> CommonEvmHandler for FungibleHandle<T>276where277 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,278{279 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueFungible.raw");280281 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {282 call::<T, UniqueFungibleCall<T>, _, _>(handle, self)283 }284}