12345678910111213141516171819extern crate alloc;20use core::char::{REPLACEMENT_CHARACTER, decode_utf16};21use core::convert::TryInto;22use evm_coder::AbiCoder;23use evm_coder::{abi::AbiType, ToLog, generate_stubgen, solidity_interface, types::*};24use up_data_structs::CollectionMode;25use pallet_common::{26 CollectionHandle,27 erc::{CommonEvmHandler, PrecompileResult, CollectionCall},28 eth::CrossAddress,29 CommonWeightInfo as _,30};31use sp_std::vec::Vec;32use pallet_evm::{account::CrossAccountId, PrecompileHandle};33use pallet_evm_coder_substrate::{34 call, dispatch_to_evm,35 execution::{PreDispatch, Result},36 frontier_contract,37};38use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};39use sp_core::{U256, Get};4041use crate::{42 Allowance, Balance, Config, FungibleHandle, Pallet, TotalSupply, SelfWeightOf,43 weights::WeightInfo, common::CommonWeights,44};4546frontier_contract! {47 macro_rules! FungibleHandle_result {...}48 impl<T: Config> Contract for FungibleHandle<T> {...}49}5051#[derive(ToLog)]52pub enum ERC20Events {53 Transfer {54 #[indexed]55 from: Address,56 #[indexed]57 to: Address,58 value: U256,59 },60 Approval {61 #[indexed]62 owner: Address,63 #[indexed]64 spender: Address,65 value: U256,66 },67}6869#[derive(AbiCoder, Debug)]70pub struct AmountForAddress {71 to: Address,72 amount: U256,73}7475#[solidity_interface(name = ERC20, events(ERC20Events), enum(derive(PreDispatch)), enum_attr(weight), expect_selector = 0x942e8b22)]76impl<T: Config> FungibleHandle<T> {77 fn name(&self) -> Result<String> {78 Ok(decode_utf16(self.name.iter().copied())79 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))80 .collect::<String>())81 }82 fn symbol(&self) -> Result<String> {83 Ok(String::from_utf8_lossy(&self.token_prefix).into())84 }85 fn total_supply(&self) -> Result<U256> {86 self.consume_store_reads(1)?;87 Ok(<TotalSupply<T>>::get(self.id).into())88 }8990 fn decimals(&self) -> Result<u8> {91 Ok(if let CollectionMode::Fungible(decimals) = &self.mode {92 *decimals93 } else {94 unreachable!()95 })96 }97 fn balance_of(&self, owner: Address) -> Result<U256> {98 self.consume_store_reads(1)?;99 let owner = T::CrossAccountId::from_eth(owner);100 let balance = <Balance<T>>::get((self.id, owner));101 Ok(balance.into())102 }103 #[weight(<CommonWeights<T>>::transfer())]104 fn transfer(&mut self, caller: Caller, to: Address, amount: U256) -> Result<bool> {105 let caller = T::CrossAccountId::from_eth(caller);106 let to = T::CrossAccountId::from_eth(to);107 let amount = amount.try_into().map_err(|_| "amount overflow")?;108 let budget = self109 .recorder110 .weight_calls_budget(<StructureWeight<T>>::find_parent());111112 <Pallet<T>>::transfer(self, &caller, &to, amount, &budget)113 .map_err(|e| dispatch_to_evm::<T>(e.error))?;114 Ok(true)115 }116117 #[weight(<CommonWeights<T>>::transfer_from())]118 fn transfer_from(119 &mut self,120 caller: Caller,121 from: Address,122 to: Address,123 amount: U256,124 ) -> Result<bool> {125 let caller = T::CrossAccountId::from_eth(caller);126 let from = T::CrossAccountId::from_eth(from);127 let to = T::CrossAccountId::from_eth(to);128 let amount = amount.try_into().map_err(|_| "amount overflow")?;129 let budget = self130 .recorder131 .weight_calls_budget(<StructureWeight<T>>::find_parent());132133 <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)134 .map_err(|e| dispatch_to_evm::<T>(e.error))?;135 Ok(true)136 }137 #[weight(<SelfWeightOf<T>>::approve())]138 fn approve(&mut self, caller: Caller, spender: Address, amount: U256) -> Result<bool> {139 let caller = T::CrossAccountId::from_eth(caller);140 let spender = T::CrossAccountId::from_eth(spender);141 let amount = amount.try_into().map_err(|_| "amount overflow")?;142143 <Pallet<T>>::set_allowance(self, &caller, &spender, amount)144 .map_err(dispatch_to_evm::<T>)?;145 Ok(true)146 }147 fn allowance(&self, owner: Address, spender: Address) -> Result<U256> {148 self.consume_store_reads(1)?;149 let owner = T::CrossAccountId::from_eth(owner);150 let spender = T::CrossAccountId::from_eth(spender);151152 Ok(<Allowance<T>>::get((self.id, owner, spender)).into())153 }154}155156#[solidity_interface(name = ERC20Mintable, enum(derive(PreDispatch)), enum_attr(weight))]157impl<T: Config> FungibleHandle<T> {158 159 160 161 #[weight(<SelfWeightOf<T>>::create_item())]162 fn mint(&mut self, caller: Caller, to: Address, amount: U256) -> Result<bool> {163 let caller = T::CrossAccountId::from_eth(caller);164 let to = T::CrossAccountId::from_eth(to);165 let amount = amount.try_into().map_err(|_| "amount overflow")?;166 let budget = self167 .recorder168 .weight_calls_budget(<StructureWeight<T>>::find_parent());169 <Pallet<T>>::create_item(self, &caller, (to, amount), &budget)170 .map_err(dispatch_to_evm::<T>)?;171 Ok(true)172 }173}174175#[solidity_interface(name = ERC20UniqueExtensions, enum(derive(PreDispatch)), enum_attr(weight))]176impl<T: Config> FungibleHandle<T>177where178 T::AccountId: From<[u8; 32]>,179{180 181 182 183 184 fn allowance_cross(&self, owner: CrossAddress, spender: CrossAddress) -> Result<U256> {185 let owner = owner.into_sub_cross_account::<T>()?;186 let spender = spender.into_sub_cross_account::<T>()?;187188 Ok(<Allowance<T>>::get((self.id, owner, spender)).into())189 }190191 192 fn description(&self) -> String {193 decode_utf16(self.description.iter().copied())194 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))195 .collect::<String>()196 }197198 #[weight(<SelfWeightOf<T>>::create_item())]199 fn mint_cross(&mut self, caller: Caller, to: CrossAddress, amount: U256) -> 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: 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: 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(<CommonWeights<T>>::transfer())]295 fn transfer_cross(&mut self, caller: Caller, to: CrossAddress, amount: U256) -> Result<bool> {296 let caller = T::CrossAccountId::from_eth(caller);297 let to = to.into_sub_cross_account::<T>()?;298 let amount = amount.try_into().map_err(|_| "amount overflow")?;299 let budget = self300 .recorder301 .weight_calls_budget(<StructureWeight<T>>::find_parent());302303 <Pallet<T>>::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?;304 Ok(true)305 }306307 #[weight(<CommonWeights<T>>::transfer_from())]308 fn transfer_from_cross(309 &mut self,310 caller: Caller,311 from: CrossAddress,312 to: CrossAddress,313 amount: U256,314 ) -> Result<bool> {315 let caller = T::CrossAccountId::from_eth(caller);316 let from = from.into_sub_cross_account::<T>()?;317 let to = to.into_sub_cross_account::<T>()?;318 let amount = amount.try_into().map_err(|_| "amount overflow")?;319 let budget = self320 .recorder321 .weight_calls_budget(<StructureWeight<T>>::find_parent());322323 <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)324 .map_err(|e| dispatch_to_evm::<T>(e.error))?;325 Ok(true)326 }327328 329 fn collection_helper_address(&self) -> Result<Address> {330 Ok(T::ContractAddress::get())331 }332333 334 335 336 fn balance_of_cross(&self, owner: CrossAddress) -> Result<U256> {337 self.consume_store_reads(1)?;338 let balance = <Balance<T>>::get((self.id, owner.into_sub_cross_account::<T>()?));339 Ok(balance.into())340 }341}342343#[solidity_interface(344 name = UniqueFungible,345 is(346 ERC20,347 ERC20Mintable,348 ERC20UniqueExtensions,349 Collection(via(common_mut returns CollectionHandle<T>)),350 ),351 enum(derive(PreDispatch))352)]353impl<T: Config> FungibleHandle<T> where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]> {}354355generate_stubgen!(gen_impl, UniqueFungibleCall<()>, true);356generate_stubgen!(gen_iface, UniqueFungibleCall<()>, false);357358impl<T: Config> CommonEvmHandler for FungibleHandle<T>359where360 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,361{362 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueFungible.raw");363364 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {365 call::<T, UniqueFungibleCall<T>, _, _>(handle, self)366 }367}