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::erc::{CommonEvmHandler, PrecompileResult};28use sp_std::vec::Vec;29use pallet_evm::{account::CrossAccountId, PrecompileHandle};30use pallet_evm_coder_substrate::{call, dispatch_to_evm};31use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};32use pallet_common::{CollectionHandle, erc::CollectionCall};3334use crate::{35 Allowance, Balance, Config, FungibleHandle, Pallet, SelfWeightOf, TotalSupply,36 weights::WeightInfo,37};3839#[derive(ToLog)]40pub enum ERC20Events {41 Transfer {42 #[indexed]43 from: address,44 #[indexed]45 to: address,46 value: uint256,47 },48 Approval {49 #[indexed]50 owner: address,51 #[indexed]52 spender: address,53 value: uint256,54 },55}5657#[solidity_interface(name = ERC20, events(ERC20Events))]58impl<T: Config> FungibleHandle<T> {59 fn name(&self) -> Result<string> {60 Ok(decode_utf16(self.name.iter().copied())61 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))62 .collect::<string>())63 }64 fn symbol(&self) -> Result<string> {65 Ok(string::from_utf8_lossy(&self.token_prefix).into())66 }67 fn total_supply(&self) -> Result<uint256> {68 self.consume_store_reads(1)?;69 Ok(<TotalSupply<T>>::get(self.id).into())70 }7172 fn decimals(&self) -> Result<uint8> {73 Ok(if let CollectionMode::Fungible(decimals) = &self.mode {74 *decimals75 } else {76 unreachable!()77 })78 }79 fn balance_of(&self, owner: address) -> Result<uint256> {80 self.consume_store_reads(1)?;81 let owner = T::CrossAccountId::from_eth(owner);82 let balance = <Balance<T>>::get((self.id, owner));83 Ok(balance.into())84 }85 #[weight(<SelfWeightOf<T>>::transfer())]86 fn transfer(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {87 let caller = T::CrossAccountId::from_eth(caller);88 let to = T::CrossAccountId::from_eth(to);89 let amount = amount.try_into().map_err(|_| "amount overflow")?;90 let budget = self91 .recorder92 .weight_calls_budget(<StructureWeight<T>>::find_parent());9394 <Pallet<T>>::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?;95 Ok(true)96 }9798 #[weight(<SelfWeightOf<T>>::transfer_from())]99 fn transfer_from(100 &mut self,101 caller: caller,102 from: address,103 to: address,104 amount: uint256,105 ) -> Result<bool> {106 let caller = T::CrossAccountId::from_eth(caller);107 let from = T::CrossAccountId::from_eth(from);108 let to = T::CrossAccountId::from_eth(to);109 let amount = amount.try_into().map_err(|_| "amount overflow")?;110 let budget = self111 .recorder112 .weight_calls_budget(<StructureWeight<T>>::find_parent());113114 <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)115 .map_err(dispatch_to_evm::<T>)?;116 Ok(true)117 }118 #[weight(<SelfWeightOf<T>>::approve())]119 fn approve(&mut self, caller: caller, spender: address, amount: uint256) -> Result<bool> {120 let caller = T::CrossAccountId::from_eth(caller);121 let spender = T::CrossAccountId::from_eth(spender);122 let amount = amount.try_into().map_err(|_| "amount overflow")?;123124 <Pallet<T>>::set_allowance(self, &caller, &spender, amount)125 .map_err(dispatch_to_evm::<T>)?;126 Ok(true)127 }128 fn allowance(&self, owner: address, spender: address) -> Result<uint256> {129 self.consume_store_reads(1)?;130 let owner = T::CrossAccountId::from_eth(owner);131 let spender = T::CrossAccountId::from_eth(spender);132133 Ok(<Allowance<T>>::get((self.id, owner, spender)).into())134 }135}136137#[solidity_interface(name = ERC20Mintable)]138impl<T: Config> FungibleHandle<T> {139 140 141 142 #[weight(<SelfWeightOf<T>>::create_item())]143 fn mint(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {144 let caller = T::CrossAccountId::from_eth(caller);145 let to = T::CrossAccountId::from_eth(to);146 let amount = amount.try_into().map_err(|_| "amount overflow")?;147 let budget = self148 .recorder149 .weight_calls_budget(<StructureWeight<T>>::find_parent());150 <Pallet<T>>::create_item(&self, &caller, (to, amount), &budget)151 .map_err(dispatch_to_evm::<T>)?;152 Ok(true)153 }154}155156#[solidity_interface(name = ERC20UniqueExtensions)]157impl<T: Config> FungibleHandle<T>158where159 T::AccountId: From<[u8; 32]>,160{161 #[weight(<SelfWeightOf<T>>::approve())]162 fn approve_cross(163 &mut self,164 caller: caller,165 spender: EthCrossAccount,166 amount: uint256,167 ) -> Result<bool> {168 let caller = T::CrossAccountId::from_eth(caller);169 let spender = spender.into_sub_cross_account::<T>()?;170 let amount = amount.try_into().map_err(|_| "amount overflow")?;171172 <Pallet<T>>::set_allowance(self, &caller, &spender, amount)173 .map_err(dispatch_to_evm::<T>)?;174 Ok(true)175 }176177 178 179 180 181 182 #[solidity(hide)]183 #[weight(<SelfWeightOf<T>>::burn_from())]184 fn burn_from(&mut self, caller: caller, from: address, amount: uint256) -> Result<bool> {185 let caller = T::CrossAccountId::from_eth(caller);186 let from = T::CrossAccountId::from_eth(from);187 let amount = amount.try_into().map_err(|_| "amount overflow")?;188 let budget = self189 .recorder190 .weight_calls_budget(<StructureWeight<T>>::find_parent());191192 <Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)193 .map_err(dispatch_to_evm::<T>)?;194 Ok(true)195 }196197 198 199 200 201 202 #[weight(<SelfWeightOf<T>>::burn_from())]203 fn burn_from_cross(204 &mut self,205 caller: caller,206 from: EthCrossAccount,207 amount: uint256,208 ) -> Result<bool> {209 let caller = T::CrossAccountId::from_eth(caller);210 let from = from.into_sub_cross_account::<T>()?;211 let amount = amount.try_into().map_err(|_| "amount overflow")?;212 let budget = self213 .recorder214 .weight_calls_budget(<StructureWeight<T>>::find_parent());215216 <Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)217 .map_err(dispatch_to_evm::<T>)?;218 Ok(true)219 }220221 222 223 #[weight(<SelfWeightOf<T>>::create_multiple_items_ex(amounts.len() as u32))]224 fn mint_bulk(&mut self, caller: caller, amounts: Vec<(address, uint256)>) -> Result<bool> {225 let caller = T::CrossAccountId::from_eth(caller);226 let budget = self227 .recorder228 .weight_calls_budget(<StructureWeight<T>>::find_parent());229 let amounts = amounts230 .into_iter()231 .map(|(to, amount)| {232 Ok((233 T::CrossAccountId::from_eth(to),234 amount.try_into().map_err(|_| "amount overflow")?,235 ))236 })237 .collect::<Result<_>>()?;238239 <Pallet<T>>::create_multiple_items(&self, &caller, amounts, &budget)240 .map_err(dispatch_to_evm::<T>)?;241 Ok(true)242 }243244 #[weight(<SelfWeightOf<T>>::transfer())]245 fn transfer_cross(246 &mut self,247 caller: caller,248 to: EthCrossAccount,249 amount: uint256,250 ) -> Result<bool> {251 let caller = T::CrossAccountId::from_eth(caller);252 let to = to.into_sub_cross_account::<T>()?;253 let amount = amount.try_into().map_err(|_| "amount overflow")?;254 let budget = self255 .recorder256 .weight_calls_budget(<StructureWeight<T>>::find_parent());257258 <Pallet<T>>::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?;259 Ok(true)260 }261262 #[weight(<SelfWeightOf<T>>::transfer_from())]263 fn transfer_from_cross(264 &mut self,265 caller: caller,266 from: EthCrossAccount,267 to: EthCrossAccount,268 amount: uint256,269 ) -> Result<bool> {270 let caller = T::CrossAccountId::from_eth(caller);271 let from = from.into_sub_cross_account::<T>()?;272 let to = to.into_sub_cross_account::<T>()?;273 let amount = amount.try_into().map_err(|_| "amount overflow")?;274 let budget = self275 .recorder276 .weight_calls_budget(<StructureWeight<T>>::find_parent());277278 <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)279 .map_err(dispatch_to_evm::<T>)?;280 Ok(true)281 }282}283284#[solidity_interface(285 name = UniqueFungible,286 is(287 ERC20,288 ERC20Mintable,289 ERC20UniqueExtensions,290 Collection(via(common_mut returns CollectionHandle<T>)),291 )292)]293impl<T: Config> FungibleHandle<T> where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]> {}294295generate_stubgen!(gen_impl, UniqueFungibleCall<()>, true);296generate_stubgen!(gen_iface, UniqueFungibleCall<()>, false);297298impl<T: Config> CommonEvmHandler for FungibleHandle<T>299where300 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,301{302 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueFungible.raw");303304 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {305 call::<T, UniqueFungibleCall<T>, _, _>(handle, self)306 }307}