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};30use sp_std::vec::Vec;31use pallet_evm::{account::CrossAccountId, PrecompileHandle};32use pallet_evm_coder_substrate::{33 call, dispatch_to_evm,34 execution::{PreDispatch, Result},35 frontier_contract,36};37use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};38use sp_core::{U256, Get};3940use crate::{41 Allowance, Balance, Config, FungibleHandle, Pallet, TotalSupply, SelfWeightOf,42 weights::WeightInfo,43};4445frontier_contract! {46 macro_rules! FungibleHandle_result {...}47 impl<T: Config> Contract for FungibleHandle<T> {...}48}4950#[derive(ToLog)]51pub enum ERC20Events {52 Transfer {53 #[indexed]54 from: Address,55 #[indexed]56 to: Address,57 value: U256,58 },59 Approval {60 #[indexed]61 owner: Address,62 #[indexed]63 spender: Address,64 value: U256,65 },66}6768#[derive(AbiCoder, Debug)]69pub struct AmountForAddress {70 to: Address,71 amount: U256,72}7374#[solidity_interface(name = ERC20, events(ERC20Events), enum(derive(PreDispatch)), enum_attr(weight), expect_selector = 0x942e8b22)]75impl<T: Config> FungibleHandle<T> {76 fn name(&self) -> Result<String> {77 Ok(decode_utf16(self.name.iter().copied())78 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))79 .collect::<String>())80 }81 fn symbol(&self) -> Result<String> {82 Ok(String::from_utf8_lossy(&self.token_prefix).into())83 }84 fn total_supply(&self) -> Result<U256> {85 self.consume_store_reads(1)?;86 Ok(<TotalSupply<T>>::get(self.id).into())87 }8889 fn decimals(&self) -> Result<u8> {90 Ok(if let CollectionMode::Fungible(decimals) = &self.mode {91 *decimals92 } else {93 unreachable!()94 })95 }96 fn balance_of(&self, owner: Address) -> Result<U256> {97 self.consume_store_reads(1)?;98 let owner = T::CrossAccountId::from_eth(owner);99 let balance = <Balance<T>>::get((self.id, owner));100 Ok(balance.into())101 }102 #[weight(<SelfWeightOf<T>>::transfer())]103 fn transfer(&mut self, caller: Caller, to: Address, amount: U256) -> Result<bool> {104 let caller = T::CrossAccountId::from_eth(caller);105 let to = T::CrossAccountId::from_eth(to);106 let amount = amount.try_into().map_err(|_| "amount overflow")?;107 let budget = self108 .recorder109 .weight_calls_budget(<StructureWeight<T>>::find_parent());110111 <Pallet<T>>::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?;112 Ok(true)113 }114115 #[weight(<SelfWeightOf<T>>::transfer_from())]116 fn transfer_from(117 &mut self,118 caller: Caller,119 from: Address,120 to: Address,121 amount: U256,122 ) -> Result<bool> {123 let caller = T::CrossAccountId::from_eth(caller);124 let from = T::CrossAccountId::from_eth(from);125 let to = T::CrossAccountId::from_eth(to);126 let amount = amount.try_into().map_err(|_| "amount overflow")?;127 let budget = self128 .recorder129 .weight_calls_budget(<StructureWeight<T>>::find_parent());130131 <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)132 .map_err(dispatch_to_evm::<T>)?;133 Ok(true)134 }135 #[weight(<SelfWeightOf<T>>::approve())]136 fn approve(&mut self, caller: Caller, spender: Address, amount: U256) -> Result<bool> {137 let caller = T::CrossAccountId::from_eth(caller);138 let spender = T::CrossAccountId::from_eth(spender);139 let amount = amount.try_into().map_err(|_| "amount overflow")?;140141 <Pallet<T>>::set_allowance(self, &caller, &spender, amount)142 .map_err(dispatch_to_evm::<T>)?;143 Ok(true)144 }145 fn allowance(&self, owner: Address, spender: Address) -> Result<U256> {146 self.consume_store_reads(1)?;147 let owner = T::CrossAccountId::from_eth(owner);148 let spender = T::CrossAccountId::from_eth(spender);149150 Ok(<Allowance<T>>::get((self.id, owner, spender)).into())151 }152}153154#[solidity_interface(name = ERC20Mintable, enum(derive(PreDispatch)), enum_attr(weight))]155impl<T: Config> FungibleHandle<T> {156 157 158 159 #[weight(<SelfWeightOf<T>>::create_item())]160 fn mint(&mut self, caller: Caller, to: Address, amount: U256) -> Result<bool> {161 let caller = T::CrossAccountId::from_eth(caller);162 let to = T::CrossAccountId::from_eth(to);163 let amount = amount.try_into().map_err(|_| "amount overflow")?;164 let budget = self165 .recorder166 .weight_calls_budget(<StructureWeight<T>>::find_parent());167 <Pallet<T>>::create_item(self, &caller, (to, amount), &budget)168 .map_err(dispatch_to_evm::<T>)?;169 Ok(true)170 }171}172173#[solidity_interface(name = ERC20UniqueExtensions, enum(derive(PreDispatch)), enum_attr(weight))]174impl<T: Config> FungibleHandle<T>175where176 T::AccountId: From<[u8; 32]>,177{178 179 180 181 182 fn allowance_cross(&self, owner: CrossAddress, spender: CrossAddress) -> Result<U256> {183 let owner = owner.into_sub_cross_account::<T>()?;184 let spender = spender.into_sub_cross_account::<T>()?;185186 Ok(<Allowance<T>>::get((self.id, owner, spender)).into())187 }188189 190 fn description(&self) -> String {191 decode_utf16(self.description.iter().copied())192 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))193 .collect::<String>()194 }195196 #[weight(<SelfWeightOf<T>>::create_item())]197 fn mint_cross(&mut self, caller: Caller, to: CrossAddress, amount: U256) -> Result<bool> {198 let caller = T::CrossAccountId::from_eth(caller);199 let to = to.into_sub_cross_account::<T>()?;200 let amount = amount.try_into().map_err(|_| "amount overflow")?;201 let budget = self202 .recorder203 .weight_calls_budget(<StructureWeight<T>>::find_parent());204 <Pallet<T>>::create_item(&self, &caller, (to, amount), &budget)205 .map_err(dispatch_to_evm::<T>)?;206 Ok(true)207 }208209 #[weight(<SelfWeightOf<T>>::approve())]210 fn approve_cross(211 &mut self,212 caller: Caller,213 spender: CrossAddress,214 amount: U256,215 ) -> Result<bool> {216 let caller = T::CrossAccountId::from_eth(caller);217 let spender = spender.into_sub_cross_account::<T>()?;218 let amount = amount.try_into().map_err(|_| "amount overflow")?;219220 <Pallet<T>>::set_allowance(self, &caller, &spender, amount)221 .map_err(dispatch_to_evm::<T>)?;222 Ok(true)223 }224225 226 227 228 229 230 #[solidity(hide)]231 #[weight(<SelfWeightOf<T>>::burn_from())]232 fn burn_from(&mut self, caller: Caller, from: Address, amount: U256) -> Result<bool> {233 let caller = T::CrossAccountId::from_eth(caller);234 let from = T::CrossAccountId::from_eth(from);235 let amount = amount.try_into().map_err(|_| "amount overflow")?;236 let budget = self237 .recorder238 .weight_calls_budget(<StructureWeight<T>>::find_parent());239240 <Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)241 .map_err(dispatch_to_evm::<T>)?;242 Ok(true)243 }244245 246 247 248 249 250 #[weight(<SelfWeightOf<T>>::burn_from())]251 fn burn_from_cross(252 &mut self,253 caller: Caller,254 from: CrossAddress,255 amount: U256,256 ) -> Result<bool> {257 let caller = T::CrossAccountId::from_eth(caller);258 let from = from.into_sub_cross_account::<T>()?;259 let amount = amount.try_into().map_err(|_| "amount overflow")?;260 let budget = self261 .recorder262 .weight_calls_budget(<StructureWeight<T>>::find_parent());263264 <Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)265 .map_err(dispatch_to_evm::<T>)?;266 Ok(true)267 }268269 270 271 #[weight(<SelfWeightOf<T>>::create_multiple_items_ex(amounts.len() as u32))]272 fn mint_bulk(&mut self, caller: Caller, amounts: Vec<AmountForAddress>) -> Result<bool> {273 let caller = T::CrossAccountId::from_eth(caller);274 let budget = self275 .recorder276 .weight_calls_budget(<StructureWeight<T>>::find_parent());277 let amounts = amounts278 .into_iter()279 .map(|AmountForAddress { to, amount }| {280 Ok((281 T::CrossAccountId::from_eth(to),282 amount.try_into().map_err(|_| "amount overflow")?,283 ))284 })285 .collect::<Result<_>>()?;286287 <Pallet<T>>::create_multiple_items(self, &caller, amounts, &budget)288 .map_err(dispatch_to_evm::<T>)?;289 Ok(true)290 }291292 #[weight(<SelfWeightOf<T>>::transfer())]293 fn transfer_cross(&mut self, caller: Caller, to: CrossAddress, amount: U256) -> Result<bool> {294 let caller = T::CrossAccountId::from_eth(caller);295 let to = to.into_sub_cross_account::<T>()?;296 let amount = amount.try_into().map_err(|_| "amount overflow")?;297 let budget = self298 .recorder299 .weight_calls_budget(<StructureWeight<T>>::find_parent());300301 <Pallet<T>>::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?;302 Ok(true)303 }304305 #[weight(<SelfWeightOf<T>>::transfer_from())]306 fn transfer_from_cross(307 &mut self,308 caller: Caller,309 from: CrossAddress,310 to: CrossAddress,311 amount: U256,312 ) -> Result<bool> {313 let caller = T::CrossAccountId::from_eth(caller);314 let from = from.into_sub_cross_account::<T>()?;315 let to = to.into_sub_cross_account::<T>()?;316 let amount = amount.try_into().map_err(|_| "amount overflow")?;317 let budget = self318 .recorder319 .weight_calls_budget(<StructureWeight<T>>::find_parent());320321 <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)322 .map_err(dispatch_to_evm::<T>)?;323 Ok(true)324 }325326 327 fn collection_helper_address(&self) -> Result<Address> {328 Ok(T::ContractAddress::get())329 }330}331332#[solidity_interface(333 name = UniqueFungible,334 is(335 ERC20,336 ERC20Mintable,337 ERC20UniqueExtensions,338 Collection(via(common_mut returns CollectionHandle<T>)),339 ),340 enum(derive(PreDispatch))341)]342impl<T: Config> FungibleHandle<T> where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]> {}343344generate_stubgen!(gen_impl, UniqueFungibleCall<()>, true);345generate_stubgen!(gen_iface, UniqueFungibleCall<()>, false);346347impl<T: Config> CommonEvmHandler for FungibleHandle<T>348where349 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,350{351 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueFungible.raw");352353 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {354 call::<T, UniqueFungibleCall<T>, _, _>(handle, self)355 }356}