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).map_err(|_| "transfer error")?;113 Ok(true)114 }115116 #[weight(<CommonWeights<T>>::transfer_from())]117 fn transfer_from(118 &mut self,119 caller: Caller,120 from: Address,121 to: Address,122 amount: U256,123 ) -> Result<bool> {124 let caller = T::CrossAccountId::from_eth(caller);125 let from = T::CrossAccountId::from_eth(from);126 let to = T::CrossAccountId::from_eth(to);127 let amount = amount.try_into().map_err(|_| "amount overflow")?;128 let budget = self129 .recorder130 .weight_calls_budget(<StructureWeight<T>>::find_parent());131132 <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)133 .map_err(|e| dispatch_to_evm::<T>(e.error))?;134 Ok(true)135 }136 #[weight(<SelfWeightOf<T>>::approve())]137 fn approve(&mut self, caller: Caller, spender: Address, amount: U256) -> Result<bool> {138 let caller = T::CrossAccountId::from_eth(caller);139 let spender = T::CrossAccountId::from_eth(spender);140 let amount = amount.try_into().map_err(|_| "amount overflow")?;141142 <Pallet<T>>::set_allowance(self, &caller, &spender, amount)143 .map_err(dispatch_to_evm::<T>)?;144 Ok(true)145 }146 fn allowance(&self, owner: Address, spender: Address) -> Result<U256> {147 self.consume_store_reads(1)?;148 let owner = T::CrossAccountId::from_eth(owner);149 let spender = T::CrossAccountId::from_eth(spender);150151 Ok(<Allowance<T>>::get((self.id, owner, spender)).into())152 }153}154155#[solidity_interface(name = ERC20Mintable, enum(derive(PreDispatch)), enum_attr(weight))]156impl<T: Config> FungibleHandle<T> {157 158 159 160 #[weight(<SelfWeightOf<T>>::create_item())]161 fn mint(&mut self, caller: Caller, to: Address, amount: U256) -> Result<bool> {162 let caller = T::CrossAccountId::from_eth(caller);163 let to = T::CrossAccountId::from_eth(to);164 let amount = amount.try_into().map_err(|_| "amount overflow")?;165 let budget = self166 .recorder167 .weight_calls_budget(<StructureWeight<T>>::find_parent());168 <Pallet<T>>::create_item(self, &caller, (to, amount), &budget)169 .map_err(dispatch_to_evm::<T>)?;170 Ok(true)171 }172}173174#[solidity_interface(name = ERC20UniqueExtensions, enum(derive(PreDispatch)), enum_attr(weight))]175impl<T: Config> FungibleHandle<T>176where177 T::AccountId: From<[u8; 32]>,178{179 180 181 182 183 fn allowance_cross(&self, owner: CrossAddress, spender: CrossAddress) -> Result<U256> {184 let owner = owner.into_sub_cross_account::<T>()?;185 let spender = spender.into_sub_cross_account::<T>()?;186187 Ok(<Allowance<T>>::get((self.id, owner, spender)).into())188 }189190 191 fn description(&self) -> String {192 decode_utf16(self.description.iter().copied())193 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))194 .collect::<String>()195 }196197 #[weight(<SelfWeightOf<T>>::create_item())]198 fn mint_cross(&mut self, caller: Caller, to: CrossAddress, amount: U256) -> Result<bool> {199 let caller = T::CrossAccountId::from_eth(caller);200 let to = to.into_sub_cross_account::<T>()?;201 let amount = amount.try_into().map_err(|_| "amount overflow")?;202 let budget = self203 .recorder204 .weight_calls_budget(<StructureWeight<T>>::find_parent());205 <Pallet<T>>::create_item(self, &caller, (to, amount), &budget)206 .map_err(dispatch_to_evm::<T>)?;207 Ok(true)208 }209210 #[weight(<SelfWeightOf<T>>::approve())]211 fn approve_cross(212 &mut self,213 caller: Caller,214 spender: CrossAddress,215 amount: U256,216 ) -> Result<bool> {217 let caller = T::CrossAccountId::from_eth(caller);218 let spender = spender.into_sub_cross_account::<T>()?;219 let amount = amount.try_into().map_err(|_| "amount overflow")?;220221 <Pallet<T>>::set_allowance(self, &caller, &spender, amount)222 .map_err(dispatch_to_evm::<T>)?;223 Ok(true)224 }225226 227 228 229 230 231 #[solidity(hide)]232 #[weight(<SelfWeightOf<T>>::burn_from())]233 fn burn_from(&mut self, caller: Caller, from: Address, amount: U256) -> Result<bool> {234 let caller = T::CrossAccountId::from_eth(caller);235 let from = T::CrossAccountId::from_eth(from);236 let amount = amount.try_into().map_err(|_| "amount overflow")?;237 let budget = self238 .recorder239 .weight_calls_budget(<StructureWeight<T>>::find_parent());240241 <Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)242 .map_err(dispatch_to_evm::<T>)?;243 Ok(true)244 }245246 247 248 249 250 251 #[weight(<SelfWeightOf<T>>::burn_from())]252 fn burn_from_cross(253 &mut self,254 caller: Caller,255 from: CrossAddress,256 amount: U256,257 ) -> Result<bool> {258 let caller = T::CrossAccountId::from_eth(caller);259 let from = from.into_sub_cross_account::<T>()?;260 let amount = amount.try_into().map_err(|_| "amount overflow")?;261 let budget = self262 .recorder263 .weight_calls_budget(<StructureWeight<T>>::find_parent());264265 <Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)266 .map_err(dispatch_to_evm::<T>)?;267 Ok(true)268 }269270 271 272 #[weight(<SelfWeightOf<T>>::create_multiple_items_ex(amounts.len() as u32))]273 fn mint_bulk(&mut self, caller: Caller, amounts: Vec<AmountForAddress>) -> Result<bool> {274 let caller = T::CrossAccountId::from_eth(caller);275 let budget = self276 .recorder277 .weight_calls_budget(<StructureWeight<T>>::find_parent());278 let amounts = amounts279 .into_iter()280 .map(|AmountForAddress { to, amount }| {281 Ok((282 T::CrossAccountId::from_eth(to),283 amount.try_into().map_err(|_| "amount overflow")?,284 ))285 })286 .collect::<Result<_>>()?;287288 <Pallet<T>>::create_multiple_items(self, &caller, amounts, &budget)289 .map_err(dispatch_to_evm::<T>)?;290 Ok(true)291 }292293 #[weight(<CommonWeights<T>>::transfer())]294 fn transfer_cross(&mut self, caller: Caller, to: CrossAddress, amount: U256) -> Result<bool> {295 let caller = T::CrossAccountId::from_eth(caller);296 let to = to.into_sub_cross_account::<T>()?;297 let amount = amount.try_into().map_err(|_| "amount overflow")?;298 let budget = self299 .recorder300 .weight_calls_budget(<StructureWeight<T>>::find_parent());301302 <Pallet<T>>::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?;303 Ok(true)304 }305306 #[weight(<CommonWeights<T>>::transfer_from())]307 fn transfer_from_cross(308 &mut self,309 caller: Caller,310 from: CrossAddress,311 to: CrossAddress,312 amount: U256,313 ) -> Result<bool> {314 let caller = T::CrossAccountId::from_eth(caller);315 let from = from.into_sub_cross_account::<T>()?;316 let to = to.into_sub_cross_account::<T>()?;317 let amount = amount.try_into().map_err(|_| "amount overflow")?;318 let budget = self319 .recorder320 .weight_calls_budget(<StructureWeight<T>>::find_parent());321322 <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)323 .map_err(|e| dispatch_to_evm::<T>(e.error))?;324 Ok(true)325 }326327 328 fn collection_helper_address(&self) -> Result<Address> {329 Ok(T::ContractAddress::get())330 }331332 333 334 335 fn balance_of_cross(&self, owner: CrossAddress) -> Result<U256> {336 self.consume_store_reads(1)?;337 let balance = <Balance<T>>::get((self.id, owner.into_sub_cross_account::<T>()?));338 Ok(balance.into())339 }340}341342#[solidity_interface(343 name = UniqueFungible,344 is(345 ERC20,346 ERC20Mintable,347 ERC20UniqueExtensions,348 Collection(via(common_mut returns CollectionHandle<T>)),349 ),350 enum(derive(PreDispatch))351)]352impl<T: Config> FungibleHandle<T> where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]> {}353354generate_stubgen!(gen_impl, UniqueFungibleCall<()>, true);355generate_stubgen!(gen_iface, UniqueFungibleCall<()>, false);356357impl<T: Config> CommonEvmHandler for FungibleHandle<T>358where359 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,360{361 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueFungible.raw");362363 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {364 call::<T, UniqueFungibleCall<T>, _, _>(handle, self)365 }366}