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 }9697 #[weight(<SelfWeightOf<T>>::transfer_from())]98 fn transfer_from(99 &mut self,100 caller: caller,101 from: address,102 to: address,103 amount: uint256,104 ) -> Result<bool> {105 let caller = T::CrossAccountId::from_eth(caller);106 let from = T::CrossAccountId::from_eth(from);107 let to = T::CrossAccountId::from_eth(to);108 let amount = amount.try_into().map_err(|_| "amount overflow")?;109 let budget = self110 .recorder111 .weight_calls_budget(<StructureWeight<T>>::find_parent());112113 <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)114 .map_err(dispatch_to_evm::<T>)?;115 Ok(true)116 }117 #[weight(<SelfWeightOf<T>>::approve())]118 fn approve(&mut self, caller: caller, spender: address, amount: uint256) -> Result<bool> {119 let caller = T::CrossAccountId::from_eth(caller);120 let spender = T::CrossAccountId::from_eth(spender);121 let amount = amount.try_into().map_err(|_| "amount overflow")?;122123 <Pallet<T>>::set_allowance(self, &caller, &spender, amount)124 .map_err(dispatch_to_evm::<T>)?;125 Ok(true)126 }127 fn allowance(&self, owner: address, spender: address) -> Result<uint256> {128 self.consume_store_reads(1)?;129 let owner = T::CrossAccountId::from_eth(owner);130 let spender = T::CrossAccountId::from_eth(spender);131132 Ok(<Allowance<T>>::get((self.id, owner, spender)).into())133 }134}135136#[solidity_interface(name = ERC20Mintable)]137impl<T: Config> FungibleHandle<T> {138 139 140 141 #[weight(<SelfWeightOf<T>>::create_item())]142 fn mint(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {143 let caller = T::CrossAccountId::from_eth(caller);144 let to = T::CrossAccountId::from_eth(to);145 let amount = amount.try_into().map_err(|_| "amount overflow")?;146 let budget = self147 .recorder148 .weight_calls_budget(<StructureWeight<T>>::find_parent());149 <Pallet<T>>::create_item(&self, &caller, (to, amount), &budget)150 .map_err(dispatch_to_evm::<T>)?;151 Ok(true)152 }153}154155#[solidity_interface(name = ERC20UniqueExtensions)]156impl<T: Config> FungibleHandle<T>157where158 T::AccountId: From<[u8; 32]>,159{160 #[weight(<SelfWeightOf<T>>::approve())]161 fn approve_cross(162 &mut self,163 caller: caller,164 spender: EthCrossAccount,165 amount: uint256,166 ) -> Result<bool> {167 let caller = T::CrossAccountId::from_eth(caller);168 let spender = spender.into_sub_cross_account::<T>()?;169 let amount = amount.try_into().map_err(|_| "amount overflow")?;170171 <Pallet<T>>::set_allowance(self, &caller, &spender, amount)172 .map_err(dispatch_to_evm::<T>)?;173 Ok(true)174 }175176 177 178 179 180 181 #[weight(<SelfWeightOf<T>>::burn_from())]182 fn burn_from(&mut self, caller: caller, from: address, amount: uint256) -> Result<bool> {183 let caller = T::CrossAccountId::from_eth(caller);184 let from = T::CrossAccountId::from_eth(from);185 let amount = amount.try_into().map_err(|_| "amount overflow")?;186 let budget = self187 .recorder188 .weight_calls_budget(<StructureWeight<T>>::find_parent());189190 <Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)191 .map_err(dispatch_to_evm::<T>)?;192 Ok(true)193 }194195 196 197 198 199 200 #[weight(<SelfWeightOf<T>>::burn_from())]201 fn burn_from_cross(202 &mut self,203 caller: caller,204 from: EthCrossAccount,205 amount: uint256,206 ) -> Result<bool> {207 let caller = T::CrossAccountId::from_eth(caller);208 let from = from.into_sub_cross_account::<T>()?;209 let amount = amount.try_into().map_err(|_| "amount overflow")?;210 let budget = self211 .recorder212 .weight_calls_budget(<StructureWeight<T>>::find_parent());213214 <Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)215 .map_err(dispatch_to_evm::<T>)?;216 Ok(true)217 }218219 220 221 #[weight(<SelfWeightOf<T>>::create_multiple_items_ex(amounts.len() as u32))]222 fn mint_bulk(&mut self, caller: caller, amounts: Vec<(address, uint256)>) -> Result<bool> {223 let caller = T::CrossAccountId::from_eth(caller);224 let budget = self225 .recorder226 .weight_calls_budget(<StructureWeight<T>>::find_parent());227 let amounts = amounts228 .into_iter()229 .map(|(to, amount)| {230 Ok((231 T::CrossAccountId::from_eth(to),232 amount.try_into().map_err(|_| "amount overflow")?,233 ))234 })235 .collect::<Result<_>>()?;236237 <Pallet<T>>::create_multiple_items(&self, &caller, amounts, &budget)238 .map_err(dispatch_to_evm::<T>)?;239 Ok(true)240 }241242 #[weight(<SelfWeightOf<T>>::transfer())]243 fn transfer_cross(244 &mut self,245 caller: caller,246 to: EthCrossAccount,247 amount: uint256,248 ) -> Result<bool> {249 let caller = T::CrossAccountId::from_eth(caller);250 let to = to.into_sub_cross_account::<T>()?;251 let amount = amount.try_into().map_err(|_| "amount overflow")?;252 let budget = self253 .recorder254 .weight_calls_budget(<StructureWeight<T>>::find_parent());255256 <Pallet<T>>::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?;257 Ok(true)258 }259260 #[weight(<SelfWeightOf<T>>::transfer_from())]261 fn transfer_from_cross(262 &mut self,263 caller: caller,264 from: EthCrossAccount,265 to: EthCrossAccount,266 amount: uint256,267 ) -> Result<bool> {268 let caller = T::CrossAccountId::from_eth(caller);269 let from = from.into_sub_cross_account::<T>()?;270 let to = to.into_sub_cross_account::<T>()?;271 let amount = amount.try_into().map_err(|_| "amount overflow")?;272 let budget = self273 .recorder274 .weight_calls_budget(<StructureWeight<T>>::find_parent());275276 <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)277 .map_err(dispatch_to_evm::<T>)?;278 Ok(true)279 }280}281282#[solidity_interface(283 name = UniqueFungible,284 is(285 ERC20,286 ERC20Mintable,287 ERC20UniqueExtensions,288 Collection(via(common_mut returns CollectionHandle<T>)),289 )290)]291impl<T: Config> FungibleHandle<T> where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]> {}292293generate_stubgen!(gen_impl, UniqueFungibleCall<()>, true);294generate_stubgen!(gen_iface, UniqueFungibleCall<()>, false);295296impl<T: Config> CommonEvmHandler for FungibleHandle<T>297where298 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,299{300 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueFungible.raw");301302 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {303 call::<T, UniqueFungibleCall<T>, _, _>(handle, self)304 }305}