12345678910111213141516171819extern crate alloc;20use core::char::{REPLACEMENT_CHARACTER, decode_utf16};21use core::convert::TryInto;22use evm_coder::{23 abi::AbiType, ToLog, execution::*, generate_stubgen, solidity_interface, types::*, weight,24};25use up_data_structs::CollectionMode;26use pallet_common::erc::{CommonEvmHandler, PrecompileResult};27use sp_std::vec::Vec;28use pallet_evm::{account::CrossAccountId, PrecompileHandle};29use pallet_evm_coder_substrate::{call, dispatch_to_evm};30use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};31use pallet_common::{CollectionHandle, erc::CollectionCall};3233use crate::{34 Allowance, Balance, Config, FungibleHandle, Pallet, SelfWeightOf, TotalSupply,35 weights::WeightInfo,36};3738#[derive(ToLog)]39pub enum ERC20Events {40 Transfer {41 #[indexed]42 from: address,43 #[indexed]44 to: address,45 value: uint256,46 },47 Approval {48 #[indexed]49 owner: address,50 #[indexed]51 spender: address,52 value: uint256,53 },54}5556#[solidity_interface(name = ERC20, events(ERC20Events))]57impl<T: Config> FungibleHandle<T> {58 fn name(&self) -> Result<string> {59 Ok(decode_utf16(self.name.iter().copied())60 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))61 .collect::<string>())62 }63 fn symbol(&self) -> Result<string> {64 Ok(string::from_utf8_lossy(&self.token_prefix).into())65 }66 fn total_supply(&self) -> Result<uint256> {67 self.consume_store_reads(1)?;68 Ok(<TotalSupply<T>>::get(self.id).into())69 }7071 fn decimals(&self) -> Result<uint8> {72 Ok(if let CollectionMode::Fungible(decimals) = &self.mode {73 *decimals74 } else {75 unreachable!()76 })77 }78 fn balance_of(&self, owner: address) -> Result<uint256> {79 self.consume_store_reads(1)?;80 let owner = T::CrossAccountId::from_eth(owner);81 let balance = <Balance<T>>::get((self.id, owner));82 Ok(balance.into())83 }84 #[weight(<SelfWeightOf<T>>::transfer())]85 fn transfer(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {86 let caller = T::CrossAccountId::from_eth(caller);87 let to = T::CrossAccountId::from_eth(to);88 let amount = amount.try_into().map_err(|_| "amount overflow")?;89 let budget = self90 .recorder91 .weight_calls_budget(<StructureWeight<T>>::find_parent());9293 <Pallet<T>>::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?;94 Ok(true)95 }96 #[weight(<SelfWeightOf<T>>::transfer_from())]97 fn transfer_from(98 &mut self,99 caller: caller,100 from: address,101 to: address,102 amount: uint256,103 ) -> Result<bool> {104 let caller = T::CrossAccountId::from_eth(caller);105 let from = T::CrossAccountId::from_eth(from);106 let to = T::CrossAccountId::from_eth(to);107 let amount = amount.try_into().map_err(|_| "amount overflow")?;108 let budget = self109 .recorder110 .weight_calls_budget(<StructureWeight<T>>::find_parent());111112 <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)113 .map_err(dispatch_to_evm::<T>)?;114 Ok(true)115 }116 #[weight(<SelfWeightOf<T>>::approve())]117 fn approve(&mut self, caller: caller, spender: address, amount: uint256) -> Result<bool> {118 let caller = T::CrossAccountId::from_eth(caller);119 let spender = T::CrossAccountId::from_eth(spender);120 let amount = amount.try_into().map_err(|_| "amount overflow")?;121122 <Pallet<T>>::set_allowance(self, &caller, &spender, amount)123 .map_err(dispatch_to_evm::<T>)?;124 Ok(true)125 }126 fn allowance(&self, owner: address, spender: address) -> Result<uint256> {127 self.consume_store_reads(1)?;128 let owner = T::CrossAccountId::from_eth(owner);129 let spender = T::CrossAccountId::from_eth(spender);130131 Ok(<Allowance<T>>::get((self.id, owner, spender)).into())132 }133}134135#[solidity_interface(name = ERC20Mintable)]136impl<T: Config> FungibleHandle<T> {137 138 139 140 #[weight(<SelfWeightOf<T>>::create_item())]141 fn mint(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {142 let caller = T::CrossAccountId::from_eth(caller);143 let to = T::CrossAccountId::from_eth(to);144 let amount = amount.try_into().map_err(|_| "amount overflow")?;145 let budget = self146 .recorder147 .weight_calls_budget(<StructureWeight<T>>::find_parent());148 <Pallet<T>>::create_item(&self, &caller, (to, amount), &budget)149 .map_err(dispatch_to_evm::<T>)?;150 Ok(true)151 }152}153154#[solidity_interface(name = ERC20UniqueExtensions)]155impl<T: Config> FungibleHandle<T>156where157 T::AccountId: From<[u8; 32]>,158{159 #[weight(<SelfWeightOf<T>>::approve())]160 fn approve_cross(161 &mut self,162 caller: caller,163 spender: EthCrossAccount,164 amount: uint256,165 ) -> Result<bool> {166 let caller = T::CrossAccountId::from_eth(caller);167 let spender = spender.into_sub_cross_account::<T>()?;168 let amount = amount.try_into().map_err(|_| "amount overflow")?;169170 <Pallet<T>>::set_allowance(self, &caller, &spender, amount)171 .map_err(dispatch_to_evm::<T>)?;172 Ok(true)173 }174175 176 177 178 179 180 #[weight(<SelfWeightOf<T>>::burn_from())]181 fn burn_from(&mut self, caller: caller, from: address, amount: uint256) -> Result<bool> {182 let caller = T::CrossAccountId::from_eth(caller);183 let from = T::CrossAccountId::from_eth(from);184 let amount = amount.try_into().map_err(|_| "amount overflow")?;185 let budget = self186 .recorder187 .weight_calls_budget(<StructureWeight<T>>::find_parent());188189 <Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)190 .map_err(dispatch_to_evm::<T>)?;191 Ok(true)192 }193194 195 196 197 198 199 #[weight(<SelfWeightOf<T>>::burn_from())]200 fn burn_from_cross(201 &mut self,202 caller: caller,203 from: EthCrossAccount,204 amount: uint256,205 ) -> Result<bool> {206 let caller = T::CrossAccountId::from_eth(caller);207 let from = from.into_sub_cross_account::<T>()?;208 let amount = amount.try_into().map_err(|_| "amount overflow")?;209 let budget = self210 .recorder211 .weight_calls_budget(<StructureWeight<T>>::find_parent());212213 <Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)214 .map_err(dispatch_to_evm::<T>)?;215 Ok(true)216 }217218 219 220 #[weight(<SelfWeightOf<T>>::create_multiple_items_ex(amounts.len() as u32))]221 fn mint_bulk(&mut self, caller: caller, amounts: Vec<(address, uint256)>) -> Result<bool> {222 let caller = T::CrossAccountId::from_eth(caller);223 let budget = self224 .recorder225 .weight_calls_budget(<StructureWeight<T>>::find_parent());226 let amounts = amounts227 .into_iter()228 .map(|(to, amount)| {229 Ok((230 T::CrossAccountId::from_eth(to),231 amount.try_into().map_err(|_| "amount overflow")?,232 ))233 })234 .collect::<Result<_>>()?;235236 <Pallet<T>>::create_multiple_items(&self, &caller, amounts, &budget)237 .map_err(dispatch_to_evm::<T>)?;238 Ok(true)239 }240241 #[weight(<SelfWeightOf<T>>::transfer_from())]242 fn transfer_from_cross(243 &mut self,244 caller: caller,245 from: EthCrossAccount,246 to: EthCrossAccount,247 amount: uint256,248 ) -> Result<bool> {249 let caller = T::CrossAccountId::from_eth(caller);250 let from = from.into_sub_cross_account::<T>()?;251 let to = to.into_sub_cross_account::<T>()?;252 let amount = amount.try_into().map_err(|_| "amount overflow")?;253 let budget = self254 .recorder255 .weight_calls_budget(<StructureWeight<T>>::find_parent());256257 <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)258 .map_err(dispatch_to_evm::<T>)?;259 Ok(true)260 }261}262263#[solidity_interface(264 name = UniqueFungible,265 is(266 ERC20,267 ERC20Mintable,268 ERC20UniqueExtensions,269 Collection(via(common_mut returns CollectionHandle<T>)),270 )271)]272impl<T: Config> FungibleHandle<T> where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]> {}273274generate_stubgen!(gen_impl, UniqueFungibleCall<()>, true);275generate_stubgen!(gen_iface, UniqueFungibleCall<()>, false);276277impl<T: Config> CommonEvmHandler for FungibleHandle<T>278where279 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,280{281 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueFungible.raw");282283 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {284 call::<T, UniqueFungibleCall<T>, _, _>(handle, self)285 }286}