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, solidity_interface, types::*,24 weight,25};26use up_data_structs::CollectionMode;27use pallet_common::{28 CollectionHandle,29 erc::{CommonEvmHandler, PrecompileResult, CollectionCall},30};31use sp_std::vec::Vec;32use pallet_evm::{account::CrossAccountId, PrecompileHandle};33use pallet_evm_coder_substrate::{call, dispatch_to_evm};34use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};35use sp_core::{U256, Get};3637use crate::{38 Allowance, Balance, Config, FungibleHandle, Pallet, SelfWeightOf, TotalSupply,39 weights::WeightInfo,40};4142#[derive(ToLog)]43pub enum ERC20Events {44 Transfer {45 #[indexed]46 from: Address,47 #[indexed]48 to: Address,49 value: U256,50 },51 Approval {52 #[indexed]53 owner: Address,54 #[indexed]55 spender: Address,56 value: U256,57 },58}5960#[solidity_interface(name = ERC20, events(ERC20Events), expect_selector = 0x942e8b22)]61impl<T: Config> FungibleHandle<T> {62 fn name(&self) -> Result<String> {63 Ok(decode_utf16(self.name.iter().copied())64 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))65 .collect::<String>())66 }67 fn symbol(&self) -> Result<String> {68 Ok(String::from_utf8_lossy(&self.token_prefix).into())69 }70 fn total_supply(&self) -> Result<U256> {71 self.consume_store_reads(1)?;72 Ok(<TotalSupply<T>>::get(self.id).into())73 }7475 fn decimals(&self) -> Result<u8> {76 Ok(if let CollectionMode::Fungible(decimals) = &self.mode {77 *decimals78 } else {79 unreachable!()80 })81 }82 fn balance_of(&self, owner: Address) -> Result<U256> {83 self.consume_store_reads(1)?;84 let owner = T::CrossAccountId::from_eth(owner);85 let balance = <Balance<T>>::get((self.id, owner));86 Ok(balance.into())87 }88 #[weight(<SelfWeightOf<T>>::transfer())]89 fn transfer(&mut self, caller: Caller, to: Address, amount: U256) -> Result<bool> {90 let caller = T::CrossAccountId::from_eth(caller);91 let to = T::CrossAccountId::from_eth(to);92 let amount = amount.try_into().map_err(|_| "amount overflow")?;93 let budget = self94 .recorder95 .weight_calls_budget(<StructureWeight<T>>::find_parent());9697 <Pallet<T>>::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?;98 Ok(true)99 }100101 #[weight(<SelfWeightOf<T>>::transfer_from())]102 fn transfer_from(103 &mut self,104 caller: Caller,105 from: Address,106 to: Address,107 amount: U256,108 ) -> Result<bool> {109 let caller = T::CrossAccountId::from_eth(caller);110 let from = T::CrossAccountId::from_eth(from);111 let to = T::CrossAccountId::from_eth(to);112 let amount = amount.try_into().map_err(|_| "amount overflow")?;113 let budget = self114 .recorder115 .weight_calls_budget(<StructureWeight<T>>::find_parent());116117 <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)118 .map_err(dispatch_to_evm::<T>)?;119 Ok(true)120 }121 #[weight(<SelfWeightOf<T>>::approve())]122 fn approve(&mut self, caller: Caller, spender: Address, amount: U256) -> Result<bool> {123 let caller = T::CrossAccountId::from_eth(caller);124 let spender = T::CrossAccountId::from_eth(spender);125 let amount = amount.try_into().map_err(|_| "amount overflow")?;126127 <Pallet<T>>::set_allowance(self, &caller, &spender, amount)128 .map_err(dispatch_to_evm::<T>)?;129 Ok(true)130 }131 fn allowance(&self, owner: Address, spender: Address) -> Result<U256> {132 self.consume_store_reads(1)?;133 let owner = T::CrossAccountId::from_eth(owner);134 let spender = T::CrossAccountId::from_eth(spender);135136 Ok(<Allowance<T>>::get((self.id, owner, spender)).into())137 }138}139140#[solidity_interface(name = ERC20Mintable)]141impl<T: Config> FungibleHandle<T> {142 143 144 145 #[weight(<SelfWeightOf<T>>::create_item())]146 fn mint(&mut self, caller: Caller, to: Address, amount: U256) -> Result<bool> {147 let caller = T::CrossAccountId::from_eth(caller);148 let to = T::CrossAccountId::from_eth(to);149 let amount = amount.try_into().map_err(|_| "amount overflow")?;150 let budget = self151 .recorder152 .weight_calls_budget(<StructureWeight<T>>::find_parent());153 <Pallet<T>>::create_item(&self, &caller, (to, amount), &budget)154 .map_err(dispatch_to_evm::<T>)?;155 Ok(true)156 }157}158159#[solidity_interface(name = ERC20UniqueExtensions)]160impl<T: Config> FungibleHandle<T>161where162 T::AccountId: From<[u8; 32]>,163{164 165 fn description(&self) -> Result<String> {166 Ok(decode_utf16(self.description.iter().copied())167 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))168 .collect::<String>())169 }170171 #[weight(<SelfWeightOf<T>>::create_item())]172 fn mint_cross(173 &mut self,174 caller: Caller,175 to: pallet_common::eth::CrossAddress,176 amount: U256,177 ) -> Result<bool> {178 let caller = T::CrossAccountId::from_eth(caller);179 let to = to.into_sub_cross_account::<T>()?;180 let amount = amount.try_into().map_err(|_| "amount overflow")?;181 let budget = self182 .recorder183 .weight_calls_budget(<StructureWeight<T>>::find_parent());184 <Pallet<T>>::create_item(&self, &caller, (to, amount), &budget)185 .map_err(dispatch_to_evm::<T>)?;186 Ok(true)187 }188189 #[weight(<SelfWeightOf<T>>::approve())]190 fn approve_cross(191 &mut self,192 caller: Caller,193 spender: pallet_common::eth::CrossAddress,194 amount: U256,195 ) -> Result<bool> {196 let caller = T::CrossAccountId::from_eth(caller);197 let spender = spender.into_sub_cross_account::<T>()?;198 let amount = amount.try_into().map_err(|_| "amount overflow")?;199200 <Pallet<T>>::set_allowance(self, &caller, &spender, amount)201 .map_err(dispatch_to_evm::<T>)?;202 Ok(true)203 }204205 206 207 208 209 210 #[solidity(hide)]211 #[weight(<SelfWeightOf<T>>::burn_from())]212 fn burn_from(&mut self, caller: Caller, from: Address, amount: U256) -> Result<bool> {213 let caller = T::CrossAccountId::from_eth(caller);214 let from = T::CrossAccountId::from_eth(from);215 let amount = amount.try_into().map_err(|_| "amount overflow")?;216 let budget = self217 .recorder218 .weight_calls_budget(<StructureWeight<T>>::find_parent());219220 <Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)221 .map_err(dispatch_to_evm::<T>)?;222 Ok(true)223 }224225 226 227 228 229 230 #[weight(<SelfWeightOf<T>>::burn_from())]231 fn burn_from_cross(232 &mut self,233 caller: Caller,234 from: pallet_common::eth::CrossAddress,235 amount: U256,236 ) -> Result<bool> {237 let caller = T::CrossAccountId::from_eth(caller);238 let from = from.into_sub_cross_account::<T>()?;239 let amount = amount.try_into().map_err(|_| "amount overflow")?;240 let budget = self241 .recorder242 .weight_calls_budget(<StructureWeight<T>>::find_parent());243244 <Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)245 .map_err(dispatch_to_evm::<T>)?;246 Ok(true)247 }248249 250 251 #[weight(<SelfWeightOf<T>>::create_multiple_items_ex(amounts.len() as u32))]252 fn mint_bulk(&mut self, caller: Caller, amounts: Vec<(Address, U256)>) -> Result<bool> {253 let caller = T::CrossAccountId::from_eth(caller);254 let budget = self255 .recorder256 .weight_calls_budget(<StructureWeight<T>>::find_parent());257 let amounts = amounts258 .into_iter()259 .map(|(to, amount)| {260 Ok((261 T::CrossAccountId::from_eth(to),262 amount.try_into().map_err(|_| "amount overflow")?,263 ))264 })265 .collect::<Result<_>>()?;266267 <Pallet<T>>::create_multiple_items(&self, &caller, amounts, &budget)268 .map_err(dispatch_to_evm::<T>)?;269 Ok(true)270 }271272 #[weight(<SelfWeightOf<T>>::transfer())]273 fn transfer_cross(274 &mut self,275 caller: Caller,276 to: pallet_common::eth::CrossAddress,277 amount: U256,278 ) -> Result<bool> {279 let caller = T::CrossAccountId::from_eth(caller);280 let to = to.into_sub_cross_account::<T>()?;281 let amount = amount.try_into().map_err(|_| "amount overflow")?;282 let budget = self283 .recorder284 .weight_calls_budget(<StructureWeight<T>>::find_parent());285286 <Pallet<T>>::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?;287 Ok(true)288 }289290 #[weight(<SelfWeightOf<T>>::transfer_from())]291 fn transfer_from_cross(292 &mut self,293 caller: Caller,294 from: pallet_common::eth::CrossAddress,295 to: pallet_common::eth::CrossAddress,296 amount: U256,297 ) -> Result<bool> {298 let caller = T::CrossAccountId::from_eth(caller);299 let from = from.into_sub_cross_account::<T>()?;300 let to = to.into_sub_cross_account::<T>()?;301 let amount = amount.try_into().map_err(|_| "amount overflow")?;302 let budget = self303 .recorder304 .weight_calls_budget(<StructureWeight<T>>::find_parent());305306 <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)307 .map_err(dispatch_to_evm::<T>)?;308 Ok(true)309 }310311 312 fn collection_helper_address(&self) -> Result<Address> {313 Ok(T::ContractAddress::get())314 }315}316317#[solidity_interface(318 name = UniqueFungible,319 is(320 ERC20,321 ERC20Mintable,322 ERC20UniqueExtensions,323 Collection(via(common_mut returns CollectionHandle<T>)),324 )325)]326impl<T: Config> FungibleHandle<T> where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]> {}327328generate_stubgen!(gen_impl, UniqueFungibleCall<()>, true);329generate_stubgen!(gen_iface, UniqueFungibleCall<()>, false);330331impl<T: Config> CommonEvmHandler for FungibleHandle<T>332where333 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,334{335 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueFungible.raw");336337 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {338 call::<T, UniqueFungibleCall<T>, _, _>(handle, self)339 }340}