12345678910111213141516171819extern crate alloc;20use core::char::{REPLACEMENT_CHARACTER, decode_utf16};21use core::convert::TryInto;22use evm_coder::AbiCoder;23use evm_coder::{24 abi::AbiType, ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*,25 weight,26};27use up_data_structs::CollectionMode;28use pallet_common::{29 CollectionHandle,30 erc::{CommonEvmHandler, PrecompileResult, CollectionCall},31 eth::CrossAddress,32};33use sp_std::vec::Vec;34use pallet_evm::{account::CrossAccountId, PrecompileHandle};35use pallet_evm_coder_substrate::{call, dispatch_to_evm};36use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};37use sp_core::{U256, Get};3839use crate::{40 Allowance, Balance, Config, FungibleHandle, Pallet, SelfWeightOf, TotalSupply,41 weights::WeightInfo,42};4344#[derive(ToLog)]45pub enum ERC20Events {46 Transfer {47 #[indexed]48 from: Address,49 #[indexed]50 to: Address,51 value: U256,52 },53 Approval {54 #[indexed]55 owner: Address,56 #[indexed]57 spender: Address,58 value: U256,59 },60}6162#[derive(AbiCoder, Debug)]63pub struct AmountForAddress {64 to: Address,65 amount: U256,66}6768#[solidity_interface(name = ERC20, events(ERC20Events), expect_selector = 0x942e8b22)]69impl<T: Config> FungibleHandle<T> {70 fn name(&self) -> Result<String> {71 Ok(decode_utf16(self.name.iter().copied())72 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))73 .collect::<String>())74 }75 fn symbol(&self) -> Result<String> {76 Ok(String::from_utf8_lossy(&self.token_prefix).into())77 }78 fn total_supply(&self) -> Result<U256> {79 self.consume_store_reads(1)?;80 Ok(<TotalSupply<T>>::get(self.id).into())81 }8283 fn decimals(&self) -> Result<u8> {84 Ok(if let CollectionMode::Fungible(decimals) = &self.mode {85 *decimals86 } else {87 unreachable!()88 })89 }90 fn balance_of(&self, owner: Address) -> Result<U256> {91 self.consume_store_reads(1)?;92 let owner = T::CrossAccountId::from_eth(owner);93 let balance = <Balance<T>>::get((self.id, owner));94 Ok(balance.into())95 }96 #[weight(<SelfWeightOf<T>>::transfer())]97 fn transfer(&mut self, caller: Caller, to: Address, amount: U256) -> Result<bool> {98 let caller = T::CrossAccountId::from_eth(caller);99 let to = T::CrossAccountId::from_eth(to);100 let amount = amount.try_into().map_err(|_| "amount overflow")?;101 let budget = self102 .recorder103 .weight_calls_budget(<StructureWeight<T>>::find_parent());104105 <Pallet<T>>::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?;106 Ok(true)107 }108109 #[weight(<SelfWeightOf<T>>::transfer_from())]110 fn transfer_from(111 &mut self,112 caller: Caller,113 from: Address,114 to: Address,115 amount: U256,116 ) -> Result<bool> {117 let caller = T::CrossAccountId::from_eth(caller);118 let from = T::CrossAccountId::from_eth(from);119 let to = T::CrossAccountId::from_eth(to);120 let amount = amount.try_into().map_err(|_| "amount overflow")?;121 let budget = self122 .recorder123 .weight_calls_budget(<StructureWeight<T>>::find_parent());124125 <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)126 .map_err(dispatch_to_evm::<T>)?;127 Ok(true)128 }129 #[weight(<SelfWeightOf<T>>::approve())]130 fn approve(&mut self, caller: Caller, spender: Address, amount: U256) -> Result<bool> {131 let caller = T::CrossAccountId::from_eth(caller);132 let spender = T::CrossAccountId::from_eth(spender);133 let amount = amount.try_into().map_err(|_| "amount overflow")?;134135 <Pallet<T>>::set_allowance(self, &caller, &spender, amount)136 .map_err(dispatch_to_evm::<T>)?;137 Ok(true)138 }139 fn allowance(&self, owner: Address, spender: Address) -> Result<U256> {140 self.consume_store_reads(1)?;141 let owner = T::CrossAccountId::from_eth(owner);142 let spender = T::CrossAccountId::from_eth(spender);143144 Ok(<Allowance<T>>::get((self.id, owner, spender)).into())145 }146}147148#[solidity_interface(name = ERC20Mintable)]149impl<T: Config> FungibleHandle<T> {150 151 152 153 #[weight(<SelfWeightOf<T>>::create_item())]154 fn mint(&mut self, caller: Caller, to: Address, amount: U256) -> Result<bool> {155 let caller = T::CrossAccountId::from_eth(caller);156 let to = T::CrossAccountId::from_eth(to);157 let amount = amount.try_into().map_err(|_| "amount overflow")?;158 let budget = self159 .recorder160 .weight_calls_budget(<StructureWeight<T>>::find_parent());161 <Pallet<T>>::create_item(&self, &caller, (to, amount), &budget)162 .map_err(dispatch_to_evm::<T>)?;163 Ok(true)164 }165}166167#[solidity_interface(name = ERC20UniqueExtensions)]168impl<T: Config> FungibleHandle<T>169where170 T::AccountId: From<[u8; 32]>,171{172 173 174 175 176 fn allowance_cross(&self, owner: CrossAddress, spender: CrossAddress) -> Result<U256> {177 let owner = owner.into_sub_cross_account::<T>()?;178 let spender = spender.into_sub_cross_account::<T>()?;179180 Ok(<Allowance<T>>::get((self.id, owner, spender)).into())181 }182183 184 fn description(&self) -> Result<String> {185 Ok(decode_utf16(self.description.iter().copied())186 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))187 .collect::<String>())188 }189190 #[weight(<SelfWeightOf<T>>::create_item())]191 fn mint_cross(&mut self, caller: Caller, to: CrossAddress, amount: U256) -> Result<bool> {192 let caller = T::CrossAccountId::from_eth(caller);193 let to = to.into_sub_cross_account::<T>()?;194 let amount = amount.try_into().map_err(|_| "amount overflow")?;195 let budget = self196 .recorder197 .weight_calls_budget(<StructureWeight<T>>::find_parent());198 <Pallet<T>>::create_item(&self, &caller, (to, amount), &budget)199 .map_err(dispatch_to_evm::<T>)?;200 Ok(true)201 }202203 #[weight(<SelfWeightOf<T>>::approve())]204 fn approve_cross(205 &mut self,206 caller: Caller,207 spender: CrossAddress,208 amount: U256,209 ) -> Result<bool> {210 let caller = T::CrossAccountId::from_eth(caller);211 let spender = spender.into_sub_cross_account::<T>()?;212 let amount = amount.try_into().map_err(|_| "amount overflow")?;213214 <Pallet<T>>::set_allowance(self, &caller, &spender, amount)215 .map_err(dispatch_to_evm::<T>)?;216 Ok(true)217 }218219 220 221 222 223 224 #[solidity(hide)]225 #[weight(<SelfWeightOf<T>>::burn_from())]226 fn burn_from(&mut self, caller: Caller, from: Address, amount: U256) -> Result<bool> {227 let caller = T::CrossAccountId::from_eth(caller);228 let from = T::CrossAccountId::from_eth(from);229 let amount = amount.try_into().map_err(|_| "amount overflow")?;230 let budget = self231 .recorder232 .weight_calls_budget(<StructureWeight<T>>::find_parent());233234 <Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)235 .map_err(dispatch_to_evm::<T>)?;236 Ok(true)237 }238239 240 241 242 243 244 #[weight(<SelfWeightOf<T>>::burn_from())]245 fn burn_from_cross(246 &mut self,247 caller: Caller,248 from: CrossAddress,249 amount: U256,250 ) -> Result<bool> {251 let caller = T::CrossAccountId::from_eth(caller);252 let from = from.into_sub_cross_account::<T>()?;253 let amount = amount.try_into().map_err(|_| "amount overflow")?;254 let budget = self255 .recorder256 .weight_calls_budget(<StructureWeight<T>>::find_parent());257258 <Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)259 .map_err(dispatch_to_evm::<T>)?;260 Ok(true)261 }262263 264 265 #[weight(<SelfWeightOf<T>>::create_multiple_items_ex(amounts.len() as u32))]266 fn mint_bulk(&mut self, caller: Caller, amounts: Vec<AmountForAddress>) -> Result<bool> {267 let caller = T::CrossAccountId::from_eth(caller);268 let budget = self269 .recorder270 .weight_calls_budget(<StructureWeight<T>>::find_parent());271 let amounts = amounts272 .into_iter()273 .map(|AmountForAddress { to, amount }| {274 Ok((275 T::CrossAccountId::from_eth(to),276 amount.try_into().map_err(|_| "amount overflow")?,277 ))278 })279 .collect::<Result<_>>()?;280281 <Pallet<T>>::create_multiple_items(&self, &caller, amounts, &budget)282 .map_err(dispatch_to_evm::<T>)?;283 Ok(true)284 }285286 #[weight(<SelfWeightOf<T>>::transfer())]287 fn transfer_cross(&mut self, caller: Caller, to: CrossAddress, amount: U256) -> Result<bool> {288 let caller = T::CrossAccountId::from_eth(caller);289 let to = to.into_sub_cross_account::<T>()?;290 let amount = amount.try_into().map_err(|_| "amount overflow")?;291 let budget = self292 .recorder293 .weight_calls_budget(<StructureWeight<T>>::find_parent());294295 <Pallet<T>>::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?;296 Ok(true)297 }298299 #[weight(<SelfWeightOf<T>>::transfer_from())]300 fn transfer_from_cross(301 &mut self,302 caller: Caller,303 from: CrossAddress,304 to: CrossAddress,305 amount: U256,306 ) -> Result<bool> {307 let caller = T::CrossAccountId::from_eth(caller);308 let from = from.into_sub_cross_account::<T>()?;309 let to = to.into_sub_cross_account::<T>()?;310 let amount = amount.try_into().map_err(|_| "amount overflow")?;311 let budget = self312 .recorder313 .weight_calls_budget(<StructureWeight<T>>::find_parent());314315 <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)316 .map_err(dispatch_to_evm::<T>)?;317 Ok(true)318 }319320 321 fn collection_helper_address(&self) -> Result<Address> {322 Ok(T::ContractAddress::get())323 }324}325326#[solidity_interface(327 name = UniqueFungible,328 is(329 ERC20,330 ERC20Mintable,331 ERC20UniqueExtensions,332 Collection(via(common_mut returns CollectionHandle<T>)),333 )334)]335impl<T: Config> FungibleHandle<T> where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]> {}336337generate_stubgen!(gen_impl, UniqueFungibleCall<()>, true);338generate_stubgen!(gen_iface, UniqueFungibleCall<()>, false);339340impl<T: Config> CommonEvmHandler for FungibleHandle<T>341where342 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,343{344 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueFungible.raw");345346 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {347 call::<T, UniqueFungibleCall<T>, _, _>(handle, self)348 }349}