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::erc::{CommonEvmHandler, PrecompileResult};28use sp_std::vec::Vec;29use pallet_evm::{account::CrossAccountId, PrecompileHandle};30use pallet_evm_coder_substrate::{call, dispatch_to_evm};31use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};32use pallet_common::{CollectionHandle, erc::CollectionCall};33use sp_core::Get;3435use crate::{36 Allowance, Balance, Config, FungibleHandle, Pallet, SelfWeightOf, TotalSupply,37 weights::WeightInfo,38};3940#[derive(ToLog)]41pub enum ERC20Events {42 Transfer {43 #[indexed]44 from: address,45 #[indexed]46 to: address,47 value: uint256,48 },49 Approval {50 #[indexed]51 owner: address,52 #[indexed]53 spender: address,54 value: uint256,55 },56}5758#[solidity_interface(name = ERC20, events(ERC20Events))]59impl<T: Config> FungibleHandle<T> {60 fn name(&self) -> Result<string> {61 Ok(decode_utf16(self.name.iter().copied())62 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))63 .collect::<string>())64 }65 fn symbol(&self) -> Result<string> {66 Ok(string::from_utf8_lossy(&self.token_prefix).into())67 }68 fn total_supply(&self) -> Result<uint256> {69 self.consume_store_reads(1)?;70 Ok(<TotalSupply<T>>::get(self.id).into())71 }7273 fn decimals(&self) -> Result<uint8> {74 Ok(if let CollectionMode::Fungible(decimals) = &self.mode {75 *decimals76 } else {77 unreachable!()78 })79 }80 fn balance_of(&self, owner: address) -> Result<uint256> {81 self.consume_store_reads(1)?;82 let owner = T::CrossAccountId::from_eth(owner);83 let balance = <Balance<T>>::get((self.id, owner));84 Ok(balance.into())85 }86 #[weight(<SelfWeightOf<T>>::transfer())]87 fn transfer(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {88 let caller = T::CrossAccountId::from_eth(caller);89 let to = T::CrossAccountId::from_eth(to);90 let amount = amount.try_into().map_err(|_| "amount overflow")?;91 let budget = self92 .recorder93 .weight_calls_budget(<StructureWeight<T>>::find_parent());9495 <Pallet<T>>::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?;96 Ok(true)97 }9899 #[weight(<SelfWeightOf<T>>::transfer_from())]100 fn transfer_from(101 &mut self,102 caller: caller,103 from: address,104 to: address,105 amount: uint256,106 ) -> Result<bool> {107 let caller = T::CrossAccountId::from_eth(caller);108 let from = T::CrossAccountId::from_eth(from);109 let to = T::CrossAccountId::from_eth(to);110 let amount = amount.try_into().map_err(|_| "amount overflow")?;111 let budget = self112 .recorder113 .weight_calls_budget(<StructureWeight<T>>::find_parent());114115 <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)116 .map_err(dispatch_to_evm::<T>)?;117 Ok(true)118 }119 #[weight(<SelfWeightOf<T>>::approve())]120 fn approve(&mut self, caller: caller, spender: address, amount: uint256) -> Result<bool> {121 let caller = T::CrossAccountId::from_eth(caller);122 let spender = T::CrossAccountId::from_eth(spender);123 let amount = amount.try_into().map_err(|_| "amount overflow")?;124125 <Pallet<T>>::set_allowance(self, &caller, &spender, amount)126 .map_err(dispatch_to_evm::<T>)?;127 Ok(true)128 }129 fn allowance(&self, owner: address, spender: address) -> Result<uint256> {130 self.consume_store_reads(1)?;131 let owner = T::CrossAccountId::from_eth(owner);132 let spender = T::CrossAccountId::from_eth(spender);133134 Ok(<Allowance<T>>::get((self.id, owner, spender)).into())135 }136137 138 fn collection_helper_address(&self) -> Result<address> {139 Ok(T::ContractAddress::get())140 }141}142143#[solidity_interface(name = ERC20Mintable)]144impl<T: Config> FungibleHandle<T> {145 146 147 148 #[weight(<SelfWeightOf<T>>::create_item())]149 fn mint(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {150 let caller = T::CrossAccountId::from_eth(caller);151 let to = T::CrossAccountId::from_eth(to);152 let amount = amount.try_into().map_err(|_| "amount overflow")?;153 let budget = self154 .recorder155 .weight_calls_budget(<StructureWeight<T>>::find_parent());156 <Pallet<T>>::create_item(&self, &caller, (to, amount), &budget)157 .map_err(dispatch_to_evm::<T>)?;158 Ok(true)159 }160}161162#[solidity_interface(name = ERC20UniqueExtensions)]163impl<T: Config> FungibleHandle<T>164where165 T::AccountId: From<[u8; 32]>,166{167 168 fn description(&self) -> Result<string> {169 Ok(decode_utf16(self.description.iter().copied())170 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))171 .collect::<string>())172 }173174 #[weight(<SelfWeightOf<T>>::approve())]175 fn approve_cross(176 &mut self,177 caller: caller,178 spender: EthCrossAccount,179 amount: uint256,180 ) -> Result<bool> {181 let caller = T::CrossAccountId::from_eth(caller);182 let spender = spender.into_sub_cross_account::<T>()?;183 let amount = amount.try_into().map_err(|_| "amount overflow")?;184185 <Pallet<T>>::set_allowance(self, &caller, &spender, amount)186 .map_err(dispatch_to_evm::<T>)?;187 Ok(true)188 }189190 191 192 193 194 195 #[solidity(hide)]196 #[weight(<SelfWeightOf<T>>::burn_from())]197 fn burn_from(&mut self, caller: caller, from: address, amount: uint256) -> Result<bool> {198 let caller = T::CrossAccountId::from_eth(caller);199 let from = T::CrossAccountId::from_eth(from);200 let amount = amount.try_into().map_err(|_| "amount overflow")?;201 let budget = self202 .recorder203 .weight_calls_budget(<StructureWeight<T>>::find_parent());204205 <Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)206 .map_err(dispatch_to_evm::<T>)?;207 Ok(true)208 }209210 211 212 213 214 215 #[weight(<SelfWeightOf<T>>::burn_from())]216 fn burn_from_cross(217 &mut self,218 caller: caller,219 from: EthCrossAccount,220 amount: uint256,221 ) -> Result<bool> {222 let caller = T::CrossAccountId::from_eth(caller);223 let from = from.into_sub_cross_account::<T>()?;224 let amount = amount.try_into().map_err(|_| "amount overflow")?;225 let budget = self226 .recorder227 .weight_calls_budget(<StructureWeight<T>>::find_parent());228229 <Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)230 .map_err(dispatch_to_evm::<T>)?;231 Ok(true)232 }233234 235 236 #[weight(<SelfWeightOf<T>>::create_multiple_items_ex(amounts.len() as u32))]237 fn mint_bulk(&mut self, caller: caller, amounts: Vec<(address, uint256)>) -> Result<bool> {238 let caller = T::CrossAccountId::from_eth(caller);239 let budget = self240 .recorder241 .weight_calls_budget(<StructureWeight<T>>::find_parent());242 let amounts = amounts243 .into_iter()244 .map(|(to, amount)| {245 Ok((246 T::CrossAccountId::from_eth(to),247 amount.try_into().map_err(|_| "amount overflow")?,248 ))249 })250 .collect::<Result<_>>()?;251252 <Pallet<T>>::create_multiple_items(&self, &caller, amounts, &budget)253 .map_err(dispatch_to_evm::<T>)?;254 Ok(true)255 }256257 #[weight(<SelfWeightOf<T>>::transfer())]258 fn transfer_cross(259 &mut self,260 caller: caller,261 to: EthCrossAccount,262 amount: uint256,263 ) -> Result<bool> {264 let caller = T::CrossAccountId::from_eth(caller);265 let to = to.into_sub_cross_account::<T>()?;266 let amount = amount.try_into().map_err(|_| "amount overflow")?;267 let budget = self268 .recorder269 .weight_calls_budget(<StructureWeight<T>>::find_parent());270271 <Pallet<T>>::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?;272 Ok(true)273 }274275 #[weight(<SelfWeightOf<T>>::transfer_from())]276 fn transfer_from_cross(277 &mut self,278 caller: caller,279 from: EthCrossAccount,280 to: EthCrossAccount,281 amount: uint256,282 ) -> Result<bool> {283 let caller = T::CrossAccountId::from_eth(caller);284 let from = from.into_sub_cross_account::<T>()?;285 let to = to.into_sub_cross_account::<T>()?;286 let amount = amount.try_into().map_err(|_| "amount overflow")?;287 let budget = self288 .recorder289 .weight_calls_budget(<StructureWeight<T>>::find_parent());290291 <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)292 .map_err(dispatch_to_evm::<T>)?;293 Ok(true)294 }295}296297#[solidity_interface(298 name = UniqueFungible,299 is(300 ERC20,301 ERC20Mintable,302 ERC20UniqueExtensions,303 Collection(via(common_mut returns CollectionHandle<T>)),304 )305)]306impl<T: Config> FungibleHandle<T> where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]> {}307308generate_stubgen!(gen_impl, UniqueFungibleCall<()>, true);309generate_stubgen!(gen_iface, UniqueFungibleCall<()>, false);310311impl<T: Config> CommonEvmHandler for FungibleHandle<T>312where313 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,314{315 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueFungible.raw");316317 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {318 call::<T, UniqueFungibleCall<T>, _, _>(handle, self)319 }320}