12345678910111213141516171819extern crate alloc;20use core::char::{REPLACEMENT_CHARACTER, decode_utf16};21use core::convert::TryInto;22use evm_coder::{23 abi::AbiType, ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*,24 weight,25};26use up_data_structs::CollectionMode;27use pallet_common::{28 CollectionHandle,29 erc::{CommonEvmHandler, PrecompileResult, CollectionCall},30};31use sp_std::vec::Vec;32use pallet_evm::{account::CrossAccountId, PrecompileHandle};33use pallet_evm_coder_substrate::{call, dispatch_to_evm};34use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};35use sp_core::Get;3637use crate::{38 Allowance, Balance, Config, FungibleHandle, Pallet, SelfWeightOf, TotalSupply,39 weights::WeightInfo,40};4142#[derive(ToLog)]43pub enum ERC20Events {44 Transfer {45 #[indexed]46 from: address,47 #[indexed]48 to: address,49 value: uint256,50 },51 Approval {52 #[indexed]53 owner: address,54 #[indexed]55 spender: address,56 value: uint256,57 },58}5960#[solidity_interface(name = ERC20, events(ERC20Events))]61impl<T: Config> FungibleHandle<T> {62 fn name(&self) -> Result<string> {63 Ok(decode_utf16(self.name.iter().copied())64 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))65 .collect::<string>())66 }67 fn symbol(&self) -> Result<string> {68 Ok(string::from_utf8_lossy(&self.token_prefix).into())69 }70 fn total_supply(&self) -> Result<uint256> {71 self.consume_store_reads(1)?;72 Ok(<TotalSupply<T>>::get(self.id).into())73 }7475 fn decimals(&self) -> Result<uint8> {76 Ok(if let CollectionMode::Fungible(decimals) = &self.mode {77 *decimals78 } else {79 unreachable!()80 })81 }82 fn balance_of(&self, owner: address) -> Result<uint256> {83 self.consume_store_reads(1)?;84 let owner = T::CrossAccountId::from_eth(owner);85 let balance = <Balance<T>>::get((self.id, owner));86 Ok(balance.into())87 }88 #[weight(<SelfWeightOf<T>>::transfer())]89 fn transfer(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {90 let caller = T::CrossAccountId::from_eth(caller);91 let to = T::CrossAccountId::from_eth(to);92 let amount = amount.try_into().map_err(|_| "amount overflow")?;93 let budget = self94 .recorder95 .weight_calls_budget(<StructureWeight<T>>::find_parent());9697 <Pallet<T>>::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?;98 Ok(true)99 }100101 #[weight(<SelfWeightOf<T>>::transfer_from())]102 fn transfer_from(103 &mut self,104 caller: caller,105 from: address,106 to: address,107 amount: uint256,108 ) -> Result<bool> {109 let caller = T::CrossAccountId::from_eth(caller);110 let from = T::CrossAccountId::from_eth(from);111 let to = T::CrossAccountId::from_eth(to);112 let amount = amount.try_into().map_err(|_| "amount overflow")?;113 let budget = self114 .recorder115 .weight_calls_budget(<StructureWeight<T>>::find_parent());116117 <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)118 .map_err(dispatch_to_evm::<T>)?;119 Ok(true)120 }121 #[weight(<SelfWeightOf<T>>::approve())]122 fn approve(&mut self, caller: caller, spender: address, amount: uint256) -> Result<bool> {123 let caller = T::CrossAccountId::from_eth(caller);124 let spender = T::CrossAccountId::from_eth(spender);125 let amount = amount.try_into().map_err(|_| "amount overflow")?;126127 <Pallet<T>>::set_allowance(self, &caller, &spender, amount)128 .map_err(dispatch_to_evm::<T>)?;129 Ok(true)130 }131 fn allowance(&self, owner: address, spender: address) -> Result<uint256> {132 self.consume_store_reads(1)?;133 let owner = T::CrossAccountId::from_eth(owner);134 let spender = T::CrossAccountId::from_eth(spender);135136 Ok(<Allowance<T>>::get((self.id, owner, spender)).into())137 }138139 140 fn collection_helper_address(&self) -> Result<address> {141 Ok(T::ContractAddress::get())142 }143}144145#[solidity_interface(name = ERC20Mintable)]146impl<T: Config> FungibleHandle<T> {147 148 149 150 #[weight(<SelfWeightOf<T>>::create_item())]151 fn mint(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {152 let caller = T::CrossAccountId::from_eth(caller);153 let to = T::CrossAccountId::from_eth(to);154 let amount = amount.try_into().map_err(|_| "amount overflow")?;155 let budget = self156 .recorder157 .weight_calls_budget(<StructureWeight<T>>::find_parent());158 <Pallet<T>>::create_item(&self, &caller, (to, amount), &budget)159 .map_err(dispatch_to_evm::<T>)?;160 Ok(true)161 }162}163164#[solidity_interface(name = ERC20UniqueExtensions)]165impl<T: Config> FungibleHandle<T>166where167 T::AccountId: From<[u8; 32]>,168{169 170 fn description(&self) -> Result<string> {171 Ok(decode_utf16(self.description.iter().copied())172 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))173 .collect::<string>())174 }175176 #[weight(<SelfWeightOf<T>>::create_item())]177 fn mint_cross(178 &mut self,179 caller: caller,180 to: pallet_common::eth::CrossAddress,181 amount: uint256,182 ) -> Result<bool> {183 let caller = T::CrossAccountId::from_eth(caller);184 let to = to.into_sub_cross_account::<T>()?;185 let amount = amount.try_into().map_err(|_| "amount overflow")?;186 let budget = self187 .recorder188 .weight_calls_budget(<StructureWeight<T>>::find_parent());189 <Pallet<T>>::create_item(&self, &caller, (to, amount), &budget)190 .map_err(dispatch_to_evm::<T>)?;191 Ok(true)192 }193194 #[weight(<SelfWeightOf<T>>::approve())]195 fn approve_cross(196 &mut self,197 caller: caller,198 spender: pallet_common::eth::CrossAddress,199 amount: uint256,200 ) -> Result<bool> {201 let caller = T::CrossAccountId::from_eth(caller);202 let spender = spender.into_sub_cross_account::<T>()?;203 let amount = amount.try_into().map_err(|_| "amount overflow")?;204205 <Pallet<T>>::set_allowance(self, &caller, &spender, amount)206 .map_err(dispatch_to_evm::<T>)?;207 Ok(true)208 }209210 211 212 213 214 215 #[solidity(hide)]216 #[weight(<SelfWeightOf<T>>::burn_from())]217 fn burn_from(&mut self, caller: caller, from: address, amount: uint256) -> Result<bool> {218 let caller = T::CrossAccountId::from_eth(caller);219 let from = T::CrossAccountId::from_eth(from);220 let amount = amount.try_into().map_err(|_| "amount overflow")?;221 let budget = self222 .recorder223 .weight_calls_budget(<StructureWeight<T>>::find_parent());224225 <Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)226 .map_err(dispatch_to_evm::<T>)?;227 Ok(true)228 }229230 231 232 233 234 235 #[weight(<SelfWeightOf<T>>::burn_from())]236 fn burn_from_cross(237 &mut self,238 caller: caller,239 from: pallet_common::eth::CrossAddress,240 amount: uint256,241 ) -> Result<bool> {242 let caller = T::CrossAccountId::from_eth(caller);243 let from = from.into_sub_cross_account::<T>()?;244 let amount = amount.try_into().map_err(|_| "amount overflow")?;245 let budget = self246 .recorder247 .weight_calls_budget(<StructureWeight<T>>::find_parent());248249 <Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)250 .map_err(dispatch_to_evm::<T>)?;251 Ok(true)252 }253254 255 256 #[weight(<SelfWeightOf<T>>::create_multiple_items_ex(amounts.len() as u32))]257 fn mint_bulk(&mut self, caller: caller, amounts: Vec<(address, uint256)>) -> Result<bool> {258 let caller = T::CrossAccountId::from_eth(caller);259 let budget = self260 .recorder261 .weight_calls_budget(<StructureWeight<T>>::find_parent());262 let amounts = amounts263 .into_iter()264 .map(|(to, amount)| {265 Ok((266 T::CrossAccountId::from_eth(to),267 amount.try_into().map_err(|_| "amount overflow")?,268 ))269 })270 .collect::<Result<_>>()?;271272 <Pallet<T>>::create_multiple_items(&self, &caller, amounts, &budget)273 .map_err(dispatch_to_evm::<T>)?;274 Ok(true)275 }276277 #[weight(<SelfWeightOf<T>>::transfer())]278 fn transfer_cross(279 &mut self,280 caller: caller,281 to: pallet_common::eth::CrossAddress,282 amount: uint256,283 ) -> Result<bool> {284 let caller = T::CrossAccountId::from_eth(caller);285 let to = to.into_sub_cross_account::<T>()?;286 let amount = amount.try_into().map_err(|_| "amount overflow")?;287 let budget = self288 .recorder289 .weight_calls_budget(<StructureWeight<T>>::find_parent());290291 <Pallet<T>>::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?;292 Ok(true)293 }294295 #[weight(<SelfWeightOf<T>>::transfer_from())]296 fn transfer_from_cross(297 &mut self,298 caller: caller,299 from: pallet_common::eth::CrossAddress,300 to: pallet_common::eth::CrossAddress,301 amount: uint256,302 ) -> Result<bool> {303 let caller = T::CrossAccountId::from_eth(caller);304 let from = from.into_sub_cross_account::<T>()?;305 let to = to.into_sub_cross_account::<T>()?;306 let amount = amount.try_into().map_err(|_| "amount overflow")?;307 let budget = self308 .recorder309 .weight_calls_budget(<StructureWeight<T>>::find_parent());310311 <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)312 .map_err(dispatch_to_evm::<T>)?;313 Ok(true)314 }315}316317#[solidity_interface(318 name = UniqueFungible,319 is(320 ERC20,321 ERC20Mintable,322 ERC20UniqueExtensions,323 Collection(via(common_mut returns CollectionHandle<T>)),324 )325)]326impl<T: Config> FungibleHandle<T> where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]> {}327328generate_stubgen!(gen_impl, UniqueFungibleCall<()>, true);329generate_stubgen!(gen_iface, UniqueFungibleCall<()>, false);330331impl<T: Config> CommonEvmHandler for FungibleHandle<T>332where333 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,334{335 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueFungible.raw");336337 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {338 call::<T, UniqueFungibleCall<T>, _, _>(handle, self)339 }340}