12345678910111213141516171819202122use core::{23 char::{REPLACEMENT_CHARACTER, decode_utf16},24 convert::TryInto,25 ops::Deref,26};27use evm_coder::{abi::AbiType, ToLog, generate_stubgen, solidity_interface, types::*};28use pallet_common::{29 erc::{CommonEvmHandler, PrecompileResult},30 eth::{collection_id_to_address, CrossAddress},31};32use pallet_evm::{account::CrossAccountId, PrecompileHandle};33use pallet_evm_coder_substrate::{34 call, dispatch_to_evm, WithRecorder, frontier_contract,35 execution::{Result, PreDispatch},36};37use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};38use sp_std::vec::Vec;39use sp_core::U256;40use up_data_structs::TokenId;4142use crate::{Allowance, Balance, Config, Pallet, RefungibleHandle, TotalSupply};4344454647pub struct RefungibleTokenHandle<T: Config>(pub RefungibleHandle<T>, pub TokenId);4849frontier_contract! {50 macro_rules! RefungibleTokenHandle_result {...}51 impl<T: Config> Contract for RefungibleTokenHandle<T> {...}52}5354#[solidity_interface(name = ERC1633, enum(derive(PreDispatch)), enum_attr(weight))]55impl<T: Config> RefungibleTokenHandle<T> {56 fn parent_token(&self) -> Address {57 collection_id_to_address(self.id)58 }5960 fn parent_token_id(&self) -> U256 {61 self.1.into()62 }63}6465#[derive(ToLog)]66pub enum ERC20Events {67 68 69 70 71 Transfer {72 #[indexed]73 from: Address,74 #[indexed]75 to: Address,76 value: U256,77 },78 79 80 Approval {81 #[indexed]82 owner: Address,83 #[indexed]84 spender: Address,85 value: U256,86 },87}888990919293#[solidity_interface(name = ERC20, events(ERC20Events), enum(derive(PreDispatch)), enum_attr(weight))]94impl<T: Config> RefungibleTokenHandle<T> {95 96 fn name(&self) -> String {97 decode_utf16(self.name.iter().copied())98 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))99 .collect::<String>()100 }101102 103 fn symbol(&self) -> String {104 String::from_utf8_lossy(&self.token_prefix).into()105 }106107 108 fn total_supply(&self) -> Result<U256> {109 self.consume_store_reads(1)?;110 Ok(<TotalSupply<T>>::get((self.id, self.1)).into())111 }112113 114 fn decimals(&self) -> Result<u8> {115 116 Ok(0)117 }118119 120 121 122 fn balance_of(&self, owner: Address) -> Result<U256> {123 self.consume_store_reads(1)?;124 let owner = T::CrossAccountId::from_eth(owner);125 let balance = <Balance<T>>::get((self.id, self.1, owner));126 Ok(balance.into())127 }128129 130 131 132 #[weight(<CommonWeights<T>>::transfer())]133 fn transfer(&mut self, caller: Caller, to: Address, amount: U256) -> Result<bool> {134 let caller = T::CrossAccountId::from_eth(caller);135 let to = T::CrossAccountId::from_eth(to);136 let amount = amount.try_into().map_err(|_| "amount overflow")?;137 let budget = self138 .recorder139 .weight_calls_budget(<StructureWeight<T>>::find_parent());140141 <Pallet<T>>::transfer(self, &caller, &to, self.1, amount, &budget)142 .map_err(dispatch_to_evm::<T>)?;143 Ok(true)144 }145146 147 148 149 150 #[weight(<CommonWeights<T>>::transfer_from())]151 fn transfer_from(152 &mut self,153 caller: Caller,154 from: Address,155 to: Address,156 amount: U256,157 ) -> Result<bool> {158 let caller = T::CrossAccountId::from_eth(caller);159 let from = T::CrossAccountId::from_eth(from);160 let to = T::CrossAccountId::from_eth(to);161 let amount = amount.try_into().map_err(|_| "amount overflow")?;162 let budget = self163 .recorder164 .weight_calls_budget(<StructureWeight<T>>::find_parent());165166 <Pallet<T>>::transfer_from(self, &caller, &from, &to, self.1, amount, &budget)167 .map_err(dispatch_to_evm::<T>)?;168 Ok(true)169 }170171 172 173 174 175 176 177 178 #[weight(<SelfWeightOf<T>>::approve())]179 fn approve(&mut self, caller: Caller, spender: Address, amount: U256) -> Result<bool> {180 let caller = T::CrossAccountId::from_eth(caller);181 let spender = T::CrossAccountId::from_eth(spender);182 let amount = amount.try_into().map_err(|_| "amount overflow")?;183184 <Pallet<T>>::set_allowance(self, &caller, &spender, self.1, amount)185 .map_err(dispatch_to_evm::<T>)?;186 Ok(true)187 }188189 190 191 192 193 fn allowance(&self, owner: Address, spender: Address) -> Result<U256> {194 self.consume_store_reads(1)?;195 let owner = T::CrossAccountId::from_eth(owner);196 let spender = T::CrossAccountId::from_eth(spender);197198 Ok(<Allowance<T>>::get((self.id, self.1, owner, spender)).into())199 }200}201202#[solidity_interface(name = ERC20UniqueExtensions, enum(derive(PreDispatch)), enum_attr(weight))]203impl<T: Config> RefungibleTokenHandle<T>204where205 T::AccountId: From<[u8; 32]>,206{207 208 209 210 211 fn allowance_cross(&self, owner: CrossAddress, spender: CrossAddress) -> Result<U256> {212 let owner = owner.into_sub_cross_account::<T>()?;213 let spender = spender.into_sub_cross_account::<T>()?;214215 Ok(<Allowance<T>>::get((self.id, self.1, owner, spender)).into())216 }217218 219 220 221 222 #[weight(<SelfWeightOf<T>>::burn_from())]223 #[solidity(hide)]224 fn burn_from(&mut self, caller: Caller, from: Address, amount: U256) -> Result<bool> {225 let caller = T::CrossAccountId::from_eth(caller);226 let from = T::CrossAccountId::from_eth(from);227 let amount = amount.try_into().map_err(|_| "amount overflow")?;228 let budget = self229 .recorder230 .weight_calls_budget(<StructureWeight<T>>::find_parent());231232 <Pallet<T>>::burn_from(self, &caller, &from, self.1, amount, &budget)233 .map_err(dispatch_to_evm::<T>)?;234 Ok(true)235 }236237 238 239 240 241 #[weight(<SelfWeightOf<T>>::burn_from())]242 fn burn_from_cross(243 &mut self,244 caller: Caller,245 from: CrossAddress,246 amount: U256,247 ) -> Result<bool> {248 let caller = T::CrossAccountId::from_eth(caller);249 let from = from.into_sub_cross_account::<T>()?;250 let amount = amount.try_into().map_err(|_| "amount overflow")?;251 let budget = self252 .recorder253 .weight_calls_budget(<StructureWeight<T>>::find_parent());254255 <Pallet<T>>::burn_from(self, &caller, &from, self.1, amount, &budget)256 .map_err(dispatch_to_evm::<T>)?;257 Ok(true)258 }259260 261 262 263 264 265 266 267 #[weight(<SelfWeightOf<T>>::approve())]268 fn approve_cross(269 &mut self,270 caller: Caller,271 spender: CrossAddress,272 amount: U256,273 ) -> Result<bool> {274 let caller = T::CrossAccountId::from_eth(caller);275 let spender = spender.into_sub_cross_account::<T>()?;276 let amount = amount.try_into().map_err(|_| "amount overflow")?;277278 <Pallet<T>>::set_allowance(self, &caller, &spender, self.1, amount)279 .map_err(dispatch_to_evm::<T>)?;280 Ok(true)281 }282 283 284 285 #[weight(<SelfWeightOf<T>>::repartition_item())]286 fn repartition(&mut self, caller: Caller, amount: U256) -> Result<bool> {287 let caller = T::CrossAccountId::from_eth(caller);288 let amount = amount.try_into().map_err(|_| "amount overflow")?;289290 <Pallet<T>>::repartition(self, &caller, self.1, amount).map_err(dispatch_to_evm::<T>)?;291 Ok(true)292 }293294 295 296 297 #[weight(<CommonWeights<T>>::transfer())]298 fn transfer_cross(&mut self, caller: Caller, to: CrossAddress, amount: U256) -> Result<bool> {299 let caller = T::CrossAccountId::from_eth(caller);300 let to = to.into_sub_cross_account::<T>()?;301 let amount = amount.try_into().map_err(|_| "amount overflow")?;302 let budget = self303 .recorder304 .weight_calls_budget(<StructureWeight<T>>::find_parent());305306 <Pallet<T>>::transfer(self, &caller, &to, self.1, amount, &budget)307 .map_err(dispatch_to_evm::<T>)?;308 Ok(true)309 }310311 312 313 314 315 #[weight(<CommonWeights<T>>::transfer_from())]316 fn transfer_from_cross(317 &mut self,318 caller: Caller,319 from: CrossAddress,320 to: CrossAddress,321 amount: U256,322 ) -> Result<bool> {323 let caller = T::CrossAccountId::from_eth(caller);324 let from = from.into_sub_cross_account::<T>()?;325 let to = to.into_sub_cross_account::<T>()?;326 let amount = amount.try_into().map_err(|_| "amount overflow")?;327 let budget = self328 .recorder329 .weight_calls_budget(<StructureWeight<T>>::find_parent());330331 <Pallet<T>>::transfer_from(self, &caller, &from, &to, self.1, amount, &budget)332 .map_err(dispatch_to_evm::<T>)?;333 Ok(true)334 }335}336337impl<T: Config> RefungibleTokenHandle<T> {338 pub fn into_inner(self) -> RefungibleHandle<T> {339 self.0340 }341 pub fn common_mut(&mut self) -> &mut RefungibleHandle<T> {342 &mut self.0343 }344}345346impl<T: Config> WithRecorder<T> for RefungibleTokenHandle<T> {347 fn recorder(&self) -> &pallet_evm_coder_substrate::SubstrateRecorder<T> {348 self.0.recorder()349 }350 fn into_recorder(self) -> pallet_evm_coder_substrate::SubstrateRecorder<T> {351 self.0.into_recorder()352 }353}354355impl<T: Config> Deref for RefungibleTokenHandle<T> {356 type Target = RefungibleHandle<T>;357358 fn deref(&self) -> &Self::Target {359 &self.0360 }361}362363#[solidity_interface(364 name = UniqueRefungibleToken,365 is(ERC20, ERC20UniqueExtensions, ERC1633),366 enum(derive(PreDispatch)),367)]368impl<T: Config> RefungibleTokenHandle<T> where T::AccountId: From<[u8; 32]> {}369370generate_stubgen!(gen_impl, UniqueRefungibleTokenCall<()>, true);371generate_stubgen!(gen_iface, UniqueRefungibleTokenCall<()>, false);372373impl<T: Config> CommonEvmHandler for RefungibleTokenHandle<T>374where375 T::AccountId: From<[u8; 32]>,376{377 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueRefungibleToken.raw");378379 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {380 call::<T, UniqueRefungibleTokenCall<T>, _, _>(handle, self)381 }382}