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};32use sp_std::vec::Vec;33use pallet_evm::{account::CrossAccountId, PrecompileHandle};34use pallet_evm_coder_substrate::{call, dispatch_to_evm};35use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};36use sp_core::{U256, Get};3738use crate::{39 Allowance, Balance, Config, FungibleHandle, Pallet, SelfWeightOf, TotalSupply,40 weights::WeightInfo,41};4243#[derive(ToLog)]44pub enum ERC20Events {45 Transfer {46 #[indexed]47 from: Address,48 #[indexed]49 to: Address,50 value: U256,51 },52 Approval {53 #[indexed]54 owner: Address,55 #[indexed]56 spender: Address,57 value: U256,58 },59}6061#[derive(AbiCoder, Debug)]62pub struct AmountForAddress {63 to: Address,64 amount: U256,65}6667#[solidity_interface(name = ERC20, events(ERC20Events), expect_selector = 0x942e8b22)]68impl<T: Config> FungibleHandle<T> {69 fn name(&self) -> Result<String> {70 Ok(decode_utf16(self.name.iter().copied())71 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))72 .collect::<String>())73 }74 fn symbol(&self) -> Result<String> {75 Ok(String::from_utf8_lossy(&self.token_prefix).into())76 }77 fn total_supply(&self) -> Result<U256> {78 self.consume_store_reads(1)?;79 Ok(<TotalSupply<T>>::get(self.id).into())80 }8182 fn decimals(&self) -> Result<u8> {83 Ok(if let CollectionMode::Fungible(decimals) = &self.mode {84 *decimals85 } else {86 unreachable!()87 })88 }89 fn balance_of(&self, owner: Address) -> Result<U256> {90 self.consume_store_reads(1)?;91 let owner = T::CrossAccountId::from_eth(owner);92 let balance = <Balance<T>>::get((self.id, owner));93 Ok(balance.into())94 }95 #[weight(<SelfWeightOf<T>>::transfer())]96 fn transfer(&mut self, caller: Caller, to: Address, amount: U256) -> Result<bool> {97 let caller = T::CrossAccountId::from_eth(caller);98 let to = T::CrossAccountId::from_eth(to);99 let amount = amount.try_into().map_err(|_| "amount overflow")?;100 let budget = self101 .recorder102 .weight_calls_budget(<StructureWeight<T>>::find_parent());103104 <Pallet<T>>::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?;105 Ok(true)106 }107108 #[weight(<SelfWeightOf<T>>::transfer_from())]109 fn transfer_from(110 &mut self,111 caller: Caller,112 from: Address,113 to: Address,114 amount: U256,115 ) -> Result<bool> {116 let caller = T::CrossAccountId::from_eth(caller);117 let from = T::CrossAccountId::from_eth(from);118 let to = T::CrossAccountId::from_eth(to);119 let amount = amount.try_into().map_err(|_| "amount overflow")?;120 let budget = self121 .recorder122 .weight_calls_budget(<StructureWeight<T>>::find_parent());123124 <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)125 .map_err(dispatch_to_evm::<T>)?;126 Ok(true)127 }128 #[weight(<SelfWeightOf<T>>::approve())]129 fn approve(&mut self, caller: Caller, spender: Address, amount: U256) -> Result<bool> {130 let caller = T::CrossAccountId::from_eth(caller);131 let spender = T::CrossAccountId::from_eth(spender);132 let amount = amount.try_into().map_err(|_| "amount overflow")?;133134 <Pallet<T>>::set_allowance(self, &caller, &spender, amount)135 .map_err(dispatch_to_evm::<T>)?;136 Ok(true)137 }138 fn allowance(&self, owner: Address, spender: Address) -> Result<U256> {139 self.consume_store_reads(1)?;140 let owner = T::CrossAccountId::from_eth(owner);141 let spender = T::CrossAccountId::from_eth(spender);142143 Ok(<Allowance<T>>::get((self.id, owner, spender)).into())144 }145}146147#[solidity_interface(name = ERC20Mintable)]148impl<T: Config> FungibleHandle<T> {149 150 151 152 #[weight(<SelfWeightOf<T>>::create_item())]153 fn mint(&mut self, caller: Caller, to: Address, amount: U256) -> Result<bool> {154 let caller = T::CrossAccountId::from_eth(caller);155 let to = T::CrossAccountId::from_eth(to);156 let amount = amount.try_into().map_err(|_| "amount overflow")?;157 let budget = self158 .recorder159 .weight_calls_budget(<StructureWeight<T>>::find_parent());160 <Pallet<T>>::create_item(&self, &caller, (to, amount), &budget)161 .map_err(dispatch_to_evm::<T>)?;162 Ok(true)163 }164}165166#[solidity_interface(name = ERC20UniqueExtensions)]167impl<T: Config> FungibleHandle<T>168where169 T::AccountId: From<[u8; 32]>,170{171 172 173 174 175 fn allowance_cross(176 &self,177 owner: pallet_common::eth::CrossAddress,178 spender: pallet_common::eth::CrossAddress,179 ) -> Result<U256> {180 let owner = owner.into_sub_cross_account::<T>()?;181 let spender = spender.into_sub_cross_account::<T>()?;182183 Ok(<Allowance<T>>::get((self.id, owner, spender)).into())184 }185186 187 fn description(&self) -> Result<String> {188 Ok(decode_utf16(self.description.iter().copied())189 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))190 .collect::<String>())191 }192193 #[weight(<SelfWeightOf<T>>::create_item())]194 fn mint_cross(195 &mut self,196 caller: Caller,197 to: pallet_common::eth::CrossAddress,198 amount: U256,199 ) -> Result<bool> {200 let caller = T::CrossAccountId::from_eth(caller);201 let to = to.into_sub_cross_account::<T>()?;202 let amount = amount.try_into().map_err(|_| "amount overflow")?;203 let budget = self204 .recorder205 .weight_calls_budget(<StructureWeight<T>>::find_parent());206 <Pallet<T>>::create_item(&self, &caller, (to, amount), &budget)207 .map_err(dispatch_to_evm::<T>)?;208 Ok(true)209 }210211 #[weight(<SelfWeightOf<T>>::approve())]212 fn approve_cross(213 &mut self,214 caller: Caller,215 spender: pallet_common::eth::CrossAddress,216 amount: U256,217 ) -> Result<bool> {218 let caller = T::CrossAccountId::from_eth(caller);219 let spender = spender.into_sub_cross_account::<T>()?;220 let amount = amount.try_into().map_err(|_| "amount overflow")?;221222 <Pallet<T>>::set_allowance(self, &caller, &spender, amount)223 .map_err(dispatch_to_evm::<T>)?;224 Ok(true)225 }226227 228 229 230 231 232 #[solidity(hide)]233 #[weight(<SelfWeightOf<T>>::burn_from())]234 fn burn_from(&mut self, caller: Caller, from: Address, amount: U256) -> Result<bool> {235 let caller = T::CrossAccountId::from_eth(caller);236 let from = T::CrossAccountId::from_eth(from);237 let amount = amount.try_into().map_err(|_| "amount overflow")?;238 let budget = self239 .recorder240 .weight_calls_budget(<StructureWeight<T>>::find_parent());241242 <Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)243 .map_err(dispatch_to_evm::<T>)?;244 Ok(true)245 }246247 248 249 250 251 252 #[weight(<SelfWeightOf<T>>::burn_from())]253 fn burn_from_cross(254 &mut self,255 caller: Caller,256 from: pallet_common::eth::CrossAddress,257 amount: U256,258 ) -> Result<bool> {259 let caller = T::CrossAccountId::from_eth(caller);260 let from = from.into_sub_cross_account::<T>()?;261 let amount = amount.try_into().map_err(|_| "amount overflow")?;262 let budget = self263 .recorder264 .weight_calls_budget(<StructureWeight<T>>::find_parent());265266 <Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)267 .map_err(dispatch_to_evm::<T>)?;268 Ok(true)269 }270271 272 273 #[weight(<SelfWeightOf<T>>::create_multiple_items_ex(amounts.len() as u32))]274 fn mint_bulk(&mut self, caller: Caller, amounts: Vec<AmountForAddress>) -> Result<bool> {275 let caller = T::CrossAccountId::from_eth(caller);276 let budget = self277 .recorder278 .weight_calls_budget(<StructureWeight<T>>::find_parent());279 let amounts = amounts280 .into_iter()281 .map(|AmountForAddress { to, amount }| {282 Ok((283 T::CrossAccountId::from_eth(to),284 amount.try_into().map_err(|_| "amount overflow")?,285 ))286 })287 .collect::<Result<_>>()?;288289 <Pallet<T>>::create_multiple_items(&self, &caller, amounts, &budget)290 .map_err(dispatch_to_evm::<T>)?;291 Ok(true)292 }293294 #[weight(<SelfWeightOf<T>>::transfer())]295 fn transfer_cross(296 &mut self,297 caller: Caller,298 to: pallet_common::eth::CrossAddress,299 amount: U256,300 ) -> Result<bool> {301 let caller = T::CrossAccountId::from_eth(caller);302 let to = to.into_sub_cross_account::<T>()?;303 let amount = amount.try_into().map_err(|_| "amount overflow")?;304 let budget = self305 .recorder306 .weight_calls_budget(<StructureWeight<T>>::find_parent());307308 <Pallet<T>>::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?;309 Ok(true)310 }311312 #[weight(<SelfWeightOf<T>>::transfer_from())]313 fn transfer_from_cross(314 &mut self,315 caller: Caller,316 from: pallet_common::eth::CrossAddress,317 to: pallet_common::eth::CrossAddress,318 amount: U256,319 ) -> Result<bool> {320 let caller = T::CrossAccountId::from_eth(caller);321 let from = from.into_sub_cross_account::<T>()?;322 let to = to.into_sub_cross_account::<T>()?;323 let amount = amount.try_into().map_err(|_| "amount overflow")?;324 let budget = self325 .recorder326 .weight_calls_budget(<StructureWeight<T>>::find_parent());327328 <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)329 .map_err(dispatch_to_evm::<T>)?;330 Ok(true)331 }332333 334 fn collection_helper_address(&self) -> Result<Address> {335 Ok(T::ContractAddress::get())336 }337}338339#[solidity_interface(340 name = UniqueFungible,341 is(342 ERC20,343 ERC20Mintable,344 ERC20UniqueExtensions,345 Collection(via(common_mut returns CollectionHandle<T>)),346 )347)]348impl<T: Config> FungibleHandle<T> where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]> {}349350generate_stubgen!(gen_impl, UniqueFungibleCall<()>, true);351generate_stubgen!(gen_iface, UniqueFungibleCall<()>, false);352353impl<T: Config> CommonEvmHandler for FungibleHandle<T>354where355 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,356{357 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueFungible.raw");358359 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {360 call::<T, UniqueFungibleCall<T>, _, _>(handle, self)361 }362}