12345678910111213141516171819extern crate alloc;20use core::{21 char::{decode_utf16, REPLACEMENT_CHARACTER},22 convert::TryInto,23};2425use evm_coder::{abi::AbiType, generate_stubgen, solidity_interface, types::*, AbiCoder, ToLog};26use pallet_common::{27 erc::{CollectionCall, CommonEvmHandler, PrecompileResult},28 eth::CrossAddress,29 CollectionHandle, CommonWeightInfo as _,30};31use pallet_evm::{account::CrossAccountId, PrecompileHandle};32use pallet_evm_coder_substrate::{33 call, dispatch_to_evm,34 execution::{PreDispatch, Result},35 frontier_contract, SubstrateRecorder,36};37use pallet_structure::{weights::WeightInfo as _, SelfWeightOf as StructureWeight};38use sp_core::{Get, U256};39use sp_std::vec::Vec;40use up_data_structs::{budget::Budget, CollectionMode};4142use crate::{43 common::CommonWeights, weights::WeightInfo, Allowance, Balance, Config, FungibleHandle, Pallet,44 SelfWeightOf, TotalSupply,45};4647frontier_contract! {48 macro_rules! FungibleHandle_result {...}49 impl<T: Config> Contract for FungibleHandle<T> {...}50}5152#[derive(ToLog)]53pub enum ERC20Events {54 Transfer {55 #[indexed]56 from: Address,57 #[indexed]58 to: Address,59 value: U256,60 },61 Approval {62 #[indexed]63 owner: Address,64 #[indexed]65 spender: Address,66 value: U256,67 },68}6970#[derive(AbiCoder, Debug)]71pub struct AmountForAddress {72 to: Address,73 amount: U256,74}7576fn nesting_budget<T: Config>(recorder: &SubstrateRecorder<T>) -> impl Budget + '_ {77 recorder.weight_calls_budget(<StructureWeight<T>>::find_parent())78}7980#[solidity_interface(name = ERC20, events(ERC20Events), enum(derive(PreDispatch)), enum_attr(weight), expect_selector = 0x942e8b22)]81impl<T: Config> FungibleHandle<T> {82 fn name(&self) -> Result<String> {83 Ok(decode_utf16(self.name.iter().copied())84 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))85 .collect::<String>())86 }87 fn symbol(&self) -> Result<String> {88 Ok(String::from_utf8_lossy(&self.token_prefix).into())89 }90 fn total_supply(&self) -> Result<U256> {91 self.consume_store_reads(1)?;92 Ok(<TotalSupply<T>>::get(self.id).into())93 }9495 fn decimals(&self) -> Result<u8> {96 Ok(if let CollectionMode::Fungible(decimals) = &self.mode {97 *decimals98 } else {99 unreachable!()100 })101 }102 fn balance_of(&self, owner: Address) -> Result<U256> {103 self.consume_store_reads(1)?;104 let owner = T::CrossAccountId::from_eth(owner);105 let balance = <Balance<T>>::get((self.id, owner));106 Ok(balance.into())107 }108 #[weight(<CommonWeights<T>>::transfer())]109 fn transfer(&mut self, caller: Caller, to: Address, amount: U256) -> Result<bool> {110 let caller = T::CrossAccountId::from_eth(caller);111 let to = T::CrossAccountId::from_eth(to);112 let amount = amount.try_into().map_err(|_| "amount overflow")?;113114 <Pallet<T>>::transfer(self, &caller, &to, amount, &nesting_budget(&self.recorder))115 .map_err(|e| dispatch_to_evm::<T>(e.error))?;116 Ok(true)117 }118119 #[weight(<CommonWeights<T>>::transfer_from())]120 fn transfer_from(121 &mut self,122 caller: Caller,123 from: Address,124 to: Address,125 amount: U256,126 ) -> Result<bool> {127 let caller = T::CrossAccountId::from_eth(caller);128 let from = T::CrossAccountId::from_eth(from);129 let to = T::CrossAccountId::from_eth(to);130 let amount = amount.try_into().map_err(|_| "amount overflow")?;131132 <Pallet<T>>::transfer_from(133 self,134 &caller,135 &from,136 &to,137 amount,138 &nesting_budget(&self.recorder),139 )140 .map_err(|e| dispatch_to_evm::<T>(e.error))?;141 Ok(true)142 }143 #[weight(<SelfWeightOf<T>>::approve())]144 fn approve(&mut self, caller: Caller, spender: Address, amount: U256) -> Result<bool> {145 let caller = T::CrossAccountId::from_eth(caller);146 let spender = T::CrossAccountId::from_eth(spender);147 let amount = amount.try_into().map_err(|_| "amount overflow")?;148149 <Pallet<T>>::set_allowance(self, &caller, &spender, amount)150 .map_err(dispatch_to_evm::<T>)?;151 Ok(true)152 }153 fn allowance(&self, owner: Address, spender: Address) -> Result<U256> {154 self.consume_store_reads(1)?;155 let owner = T::CrossAccountId::from_eth(owner);156 let spender = T::CrossAccountId::from_eth(spender);157158 Ok(<Allowance<T>>::get((self.id, owner, spender)).into())159 }160}161162#[solidity_interface(name = ERC20Mintable, enum(derive(PreDispatch)), enum_attr(weight))]163impl<T: Config> FungibleHandle<T> {164 165 166 167 #[weight(<SelfWeightOf<T>>::create_item())]168 fn mint(&mut self, caller: Caller, to: Address, amount: U256) -> Result<bool> {169 let caller = T::CrossAccountId::from_eth(caller);170 let to = T::CrossAccountId::from_eth(to);171 let amount = amount.try_into().map_err(|_| "amount overflow")?;172173 <Pallet<T>>::create_item(self, &caller, (to, amount), &nesting_budget(&self.recorder))174 .map_err(dispatch_to_evm::<T>)?;175 Ok(true)176 }177}178179#[solidity_interface(name = ERC20UniqueExtensions, enum(derive(PreDispatch)), enum_attr(weight))]180impl<T: Config> FungibleHandle<T>181where182 T::AccountId: From<[u8; 32]>,183{184 185 186 187 188 fn allowance_cross(&self, owner: CrossAddress, spender: CrossAddress) -> Result<U256> {189 let owner = owner.into_sub_cross_account::<T>()?;190 let spender = spender.into_sub_cross_account::<T>()?;191192 Ok(<Allowance<T>>::get((self.id, owner, spender)).into())193 }194195 196 fn description(&self) -> String {197 decode_utf16(self.description.iter().copied())198 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))199 .collect::<String>()200 }201202 #[weight(<SelfWeightOf<T>>::create_item())]203 fn mint_cross(&mut self, caller: Caller, to: CrossAddress, amount: U256) -> Result<bool> {204 let caller = T::CrossAccountId::from_eth(caller);205 let to = to.into_sub_cross_account::<T>()?;206 let amount = amount.try_into().map_err(|_| "amount overflow")?;207208 <Pallet<T>>::create_item(self, &caller, (to, amount), &nesting_budget(&self.recorder))209 .map_err(dispatch_to_evm::<T>)?;210 Ok(true)211 }212213 #[weight(<SelfWeightOf<T>>::approve())]214 fn approve_cross(215 &mut self,216 caller: Caller,217 spender: CrossAddress,218 amount: U256,219 ) -> Result<bool> {220 let caller = T::CrossAccountId::from_eth(caller);221 let spender = spender.into_sub_cross_account::<T>()?;222 let amount = amount.try_into().map_err(|_| "amount overflow")?;223224 <Pallet<T>>::set_allowance(self, &caller, &spender, amount)225 .map_err(dispatch_to_evm::<T>)?;226 Ok(true)227 }228229 230 231 232 233 234 #[solidity(hide)]235 #[weight(<SelfWeightOf<T>>::burn_from())]236 fn burn_from(&mut self, caller: Caller, from: Address, amount: U256) -> Result<bool> {237 let caller = T::CrossAccountId::from_eth(caller);238 let from = T::CrossAccountId::from_eth(from);239 let amount = amount.try_into().map_err(|_| "amount overflow")?;240241 <Pallet<T>>::burn_from(242 self,243 &caller,244 &from,245 amount,246 &nesting_budget(&self.recorder),247 )248 .map_err(dispatch_to_evm::<T>)?;249 Ok(true)250 }251252 253 254 255 256 257 #[weight(<SelfWeightOf<T>>::burn_from())]258 fn burn_from_cross(259 &mut self,260 caller: Caller,261 from: CrossAddress,262 amount: U256,263 ) -> Result<bool> {264 let caller = T::CrossAccountId::from_eth(caller);265 let from = from.into_sub_cross_account::<T>()?;266 let amount = amount.try_into().map_err(|_| "amount overflow")?;267268 <Pallet<T>>::burn_from(269 self,270 &caller,271 &from,272 amount,273 &nesting_budget(&self.recorder),274 )275 .map_err(dispatch_to_evm::<T>)?;276 Ok(true)277 }278279 280 281 #[weight(<SelfWeightOf<T>>::create_multiple_items_ex(amounts.len() as u32))]282 fn mint_bulk(&mut self, caller: Caller, amounts: Vec<AmountForAddress>) -> Result<bool> {283 let caller = T::CrossAccountId::from_eth(caller);284 let amounts = amounts285 .into_iter()286 .map(|AmountForAddress { to, amount }| {287 Ok((288 T::CrossAccountId::from_eth(to),289 amount.try_into().map_err(|_| "amount overflow")?,290 ))291 })292 .collect::<Result<_>>()?;293294 <Pallet<T>>::create_multiple_items(self, &caller, amounts, &nesting_budget(&self.recorder))295 .map_err(dispatch_to_evm::<T>)?;296 Ok(true)297 }298299 #[weight(<CommonWeights<T>>::transfer())]300 fn transfer_cross(&mut self, caller: Caller, to: CrossAddress, amount: U256) -> 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")?;304305 <Pallet<T>>::transfer(self, &caller, &to, amount, &nesting_budget(&self.recorder))306 .map_err(|_| "transfer error")?;307 Ok(true)308 }309310 #[weight(<CommonWeights<T>>::transfer_from())]311 fn transfer_from_cross(312 &mut self,313 caller: Caller,314 from: CrossAddress,315 to: CrossAddress,316 amount: U256,317 ) -> Result<bool> {318 let caller = T::CrossAccountId::from_eth(caller);319 let from = from.into_sub_cross_account::<T>()?;320 let to = to.into_sub_cross_account::<T>()?;321 let amount = amount.try_into().map_err(|_| "amount overflow")?;322323 <Pallet<T>>::transfer_from(324 self,325 &caller,326 &from,327 &to,328 amount,329 &nesting_budget(&self.recorder),330 )331 .map_err(|e| dispatch_to_evm::<T>(e.error))?;332 Ok(true)333 }334335 336 fn collection_helper_address(&self) -> Result<Address> {337 Ok(T::ContractAddress::get())338 }339340 341 342 343 fn balance_of_cross(&self, owner: CrossAddress) -> Result<U256> {344 self.consume_store_reads(1)?;345 let balance = <Balance<T>>::get((self.id, owner.into_sub_cross_account::<T>()?));346 Ok(balance.into())347 }348}349350#[solidity_interface(351 name = UniqueFungible,352 is(353 ERC20,354 ERC20Mintable,355 ERC20UniqueExtensions,356 Collection(via(common_mut returns CollectionHandle<T>)),357 ),358 enum(derive(PreDispatch))359)]360impl<T: Config> FungibleHandle<T> where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]> {}361362generate_stubgen!(gen_impl, UniqueFungibleCall<()>, true);363generate_stubgen!(gen_iface, UniqueFungibleCall<()>, false);364365impl<T: Config> CommonEvmHandler for FungibleHandle<T>366where367 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,368{369 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueFungible.raw");370371 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {372 call::<T, UniqueFungibleCall<T>, _, _>(handle, self)373 }374}