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,36};37use pallet_structure::{weights::WeightInfo as _, SelfWeightOf as StructureWeight};38use sp_core::{Get, U256};39use sp_std::vec::Vec;40use up_data_structs::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}7576#[solidity_interface(name = ERC20, events(ERC20Events), enum(derive(PreDispatch)), enum_attr(weight), expect_selector = 0x942e8b22)]77impl<T: Config> FungibleHandle<T> {78 fn name(&self) -> Result<String> {79 Ok(decode_utf16(self.name.iter().copied())80 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))81 .collect::<String>())82 }83 fn symbol(&self) -> Result<String> {84 Ok(String::from_utf8_lossy(&self.token_prefix).into())85 }86 fn total_supply(&self) -> Result<U256> {87 self.consume_store_reads(1)?;88 Ok(<TotalSupply<T>>::get(self.id).into())89 }9091 fn decimals(&self) -> Result<u8> {92 Ok(if let CollectionMode::Fungible(decimals) = &self.mode {93 *decimals94 } else {95 unreachable!()96 })97 }98 fn balance_of(&self, owner: Address) -> Result<U256> {99 self.consume_store_reads(1)?;100 let owner = T::CrossAccountId::from_eth(owner);101 let balance = <Balance<T>>::get((self.id, owner));102 Ok(balance.into())103 }104 #[weight(<CommonWeights<T>>::transfer())]105 fn transfer(&mut self, caller: Caller, to: Address, amount: U256) -> Result<bool> {106 let caller = T::CrossAccountId::from_eth(caller);107 let to = T::CrossAccountId::from_eth(to);108 let amount = amount.try_into().map_err(|_| "amount overflow")?;109 let budget = self110 .recorder111 .weight_calls_budget(<StructureWeight<T>>::find_parent());112113 <Pallet<T>>::transfer(self, &caller, &to, amount, &budget)114 .map_err(|e| dispatch_to_evm::<T>(e.error))?;115 Ok(true)116 }117118 #[weight(<CommonWeights<T>>::transfer_from())]119 fn transfer_from(120 &mut self,121 caller: Caller,122 from: Address,123 to: Address,124 amount: U256,125 ) -> Result<bool> {126 let caller = T::CrossAccountId::from_eth(caller);127 let from = T::CrossAccountId::from_eth(from);128 let to = T::CrossAccountId::from_eth(to);129 let amount = amount.try_into().map_err(|_| "amount overflow")?;130 let budget = self131 .recorder132 .weight_calls_budget(<StructureWeight<T>>::find_parent());133134 <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)135 .map_err(|e| dispatch_to_evm::<T>(e.error))?;136 Ok(true)137 }138 #[weight(<SelfWeightOf<T>>::approve())]139 fn approve(&mut self, caller: Caller, spender: Address, amount: U256) -> Result<bool> {140 let caller = T::CrossAccountId::from_eth(caller);141 let spender = T::CrossAccountId::from_eth(spender);142 let amount = amount.try_into().map_err(|_| "amount overflow")?;143144 <Pallet<T>>::set_allowance(self, &caller, &spender, amount)145 .map_err(dispatch_to_evm::<T>)?;146 Ok(true)147 }148 fn allowance(&self, owner: Address, spender: Address) -> Result<U256> {149 self.consume_store_reads(1)?;150 let owner = T::CrossAccountId::from_eth(owner);151 let spender = T::CrossAccountId::from_eth(spender);152153 Ok(<Allowance<T>>::get((self.id, owner, spender)).into())154 }155}156157#[solidity_interface(name = ERC20Mintable, enum(derive(PreDispatch)), enum_attr(weight))]158impl<T: Config> FungibleHandle<T> {159 160 161 162 #[weight(<SelfWeightOf<T>>::create_item())]163 fn mint(&mut self, caller: Caller, to: Address, amount: U256) -> Result<bool> {164 let caller = T::CrossAccountId::from_eth(caller);165 let to = T::CrossAccountId::from_eth(to);166 let amount = amount.try_into().map_err(|_| "amount overflow")?;167 let budget = self168 .recorder169 .weight_calls_budget(<StructureWeight<T>>::find_parent());170 <Pallet<T>>::create_item(self, &caller, (to, amount), &budget)171 .map_err(dispatch_to_evm::<T>)?;172 Ok(true)173 }174}175176#[solidity_interface(name = ERC20UniqueExtensions, enum(derive(PreDispatch)), enum_attr(weight))]177impl<T: Config> FungibleHandle<T>178where179 T::AccountId: From<[u8; 32]>,180{181 182 183 184 185 fn allowance_cross(&self, owner: CrossAddress, spender: CrossAddress) -> Result<U256> {186 let owner = owner.into_sub_cross_account::<T>()?;187 let spender = spender.into_sub_cross_account::<T>()?;188189 Ok(<Allowance<T>>::get((self.id, owner, spender)).into())190 }191192 193 fn description(&self) -> String {194 decode_utf16(self.description.iter().copied())195 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))196 .collect::<String>()197 }198199 #[weight(<SelfWeightOf<T>>::create_item())]200 fn mint_cross(&mut self, caller: Caller, to: CrossAddress, amount: U256) -> Result<bool> {201 let caller = T::CrossAccountId::from_eth(caller);202 let to = to.into_sub_cross_account::<T>()?;203 let amount = amount.try_into().map_err(|_| "amount overflow")?;204 let budget = self205 .recorder206 .weight_calls_budget(<StructureWeight<T>>::find_parent());207 <Pallet<T>>::create_item(self, &caller, (to, amount), &budget)208 .map_err(dispatch_to_evm::<T>)?;209 Ok(true)210 }211212 #[weight(<SelfWeightOf<T>>::approve())]213 fn approve_cross(214 &mut self,215 caller: Caller,216 spender: CrossAddress,217 amount: U256,218 ) -> Result<bool> {219 let caller = T::CrossAccountId::from_eth(caller);220 let spender = spender.into_sub_cross_account::<T>()?;221 let amount = amount.try_into().map_err(|_| "amount overflow")?;222223 <Pallet<T>>::set_allowance(self, &caller, &spender, amount)224 .map_err(dispatch_to_evm::<T>)?;225 Ok(true)226 }227228 229 230 231 232 233 #[solidity(hide)]234 #[weight(<SelfWeightOf<T>>::burn_from())]235 fn burn_from(&mut self, caller: Caller, from: Address, amount: U256) -> Result<bool> {236 let caller = T::CrossAccountId::from_eth(caller);237 let from = T::CrossAccountId::from_eth(from);238 let amount = amount.try_into().map_err(|_| "amount overflow")?;239 let budget = self240 .recorder241 .weight_calls_budget(<StructureWeight<T>>::find_parent());242243 <Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)244 .map_err(dispatch_to_evm::<T>)?;245 Ok(true)246 }247248 249 250 251 252 253 #[weight(<SelfWeightOf<T>>::burn_from())]254 fn burn_from_cross(255 &mut self,256 caller: Caller,257 from: CrossAddress,258 amount: U256,259 ) -> Result<bool> {260 let caller = T::CrossAccountId::from_eth(caller);261 let from = from.into_sub_cross_account::<T>()?;262 let amount = amount.try_into().map_err(|_| "amount overflow")?;263 let budget = self264 .recorder265 .weight_calls_budget(<StructureWeight<T>>::find_parent());266267 <Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)268 .map_err(dispatch_to_evm::<T>)?;269 Ok(true)270 }271272 273 274 #[weight(<SelfWeightOf<T>>::create_multiple_items_ex(amounts.len() as u32))]275 fn mint_bulk(&mut self, caller: Caller, amounts: Vec<AmountForAddress>) -> Result<bool> {276 let caller = T::CrossAccountId::from_eth(caller);277 let budget = self278 .recorder279 .weight_calls_budget(<StructureWeight<T>>::find_parent());280 let amounts = amounts281 .into_iter()282 .map(|AmountForAddress { to, amount }| {283 Ok((284 T::CrossAccountId::from_eth(to),285 amount.try_into().map_err(|_| "amount overflow")?,286 ))287 })288 .collect::<Result<_>>()?;289290 <Pallet<T>>::create_multiple_items(self, &caller, amounts, &budget)291 .map_err(dispatch_to_evm::<T>)?;292 Ok(true)293 }294295 #[weight(<CommonWeights<T>>::transfer())]296 fn transfer_cross(&mut self, caller: Caller, to: CrossAddress, amount: U256) -> Result<bool> {297 let caller = T::CrossAccountId::from_eth(caller);298 let to = to.into_sub_cross_account::<T>()?;299 let amount = amount.try_into().map_err(|_| "amount overflow")?;300 let budget = self301 .recorder302 .weight_calls_budget(<StructureWeight<T>>::find_parent());303304 <Pallet<T>>::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?;305 Ok(true)306 }307308 #[weight(<CommonWeights<T>>::transfer_from())]309 fn transfer_from_cross(310 &mut self,311 caller: Caller,312 from: CrossAddress,313 to: CrossAddress,314 amount: U256,315 ) -> Result<bool> {316 let caller = T::CrossAccountId::from_eth(caller);317 let from = from.into_sub_cross_account::<T>()?;318 let to = to.into_sub_cross_account::<T>()?;319 let amount = amount.try_into().map_err(|_| "amount overflow")?;320 let budget = self321 .recorder322 .weight_calls_budget(<StructureWeight<T>>::find_parent());323324 <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)325 .map_err(|e| dispatch_to_evm::<T>(e.error))?;326 Ok(true)327 }328329 330 fn collection_helper_address(&self) -> Result<Address> {331 Ok(T::ContractAddress::get())332 }333334 335 336 337 fn balance_of_cross(&self, owner: CrossAddress) -> Result<U256> {338 self.consume_store_reads(1)?;339 let balance = <Balance<T>>::get((self.id, owner.into_sub_cross_account::<T>()?));340 Ok(balance.into())341 }342}343344#[solidity_interface(345 name = UniqueFungible,346 is(347 ERC20,348 ERC20Mintable,349 ERC20UniqueExtensions,350 Collection(via(common_mut returns CollectionHandle<T>)),351 ),352 enum(derive(PreDispatch))353)]354impl<T: Config> FungibleHandle<T> where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]> {}355356generate_stubgen!(gen_impl, UniqueFungibleCall<()>, true);357generate_stubgen!(gen_iface, UniqueFungibleCall<()>, false);358359impl<T: Config> CommonEvmHandler for FungibleHandle<T>360where361 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,362{363 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueFungible.raw");364365 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {366 call::<T, UniqueFungibleCall<T>, _, _>(handle, self)367 }368}