12345678910111213141516171819202122use core::{23 char::{decode_utf16, REPLACEMENT_CHARACTER},24 convert::TryInto,25 ops::Deref,26};2728use evm_coder::{abi::AbiType, generate_stubgen, solidity_interface, types::*, ToLog};29use pallet_common::{30 erc::{CommonEvmHandler, PrecompileResult},31 eth::{collection_id_to_address, CrossAddress},32 CommonWeightInfo,33};34use pallet_evm::{account::CrossAccountId, PrecompileHandle};35use pallet_evm_coder_substrate::{36 call, dispatch_to_evm,37 execution::{PreDispatch, Result},38 frontier_contract, WithRecorder,39};40use pallet_structure::{weights::WeightInfo as _, SelfWeightOf as StructureWeight};41use sp_core::U256;42use sp_std::vec::Vec;43use up_data_structs::TokenId;4445use crate::{46 common::CommonWeights, weights::WeightInfo, Allowance, Balance, Config, Pallet,47 RefungibleHandle, SelfWeightOf, TotalSupply,48};4950515253pub struct RefungibleTokenHandle<T: Config>(pub RefungibleHandle<T>, pub TokenId);5455frontier_contract! {56 macro_rules! RefungibleTokenHandle_result {...}57 impl<T: Config> Contract for RefungibleTokenHandle<T> {...}58}5960#[solidity_interface(name = ERC1633, enum(derive(PreDispatch)), enum_attr(weight))]61impl<T: Config> RefungibleTokenHandle<T> {62 fn parent_token(&self) -> Address {63 collection_id_to_address(self.id)64 }6566 fn parent_token_id(&self) -> U256 {67 self.1.into()68 }69}7071#[derive(ToLog)]72pub enum ERC20Events {73 74 75 76 77 Transfer {78 #[indexed]79 from: Address,80 #[indexed]81 to: Address,82 value: U256,83 },84 85 86 Approval {87 #[indexed]88 owner: Address,89 #[indexed]90 spender: Address,91 value: U256,92 },93}949596979899#[solidity_interface(name = ERC20, events(ERC20Events), enum(derive(PreDispatch)), enum_attr(weight))]100impl<T: Config> RefungibleTokenHandle<T> {101 102 fn name(&self) -> String {103 decode_utf16(self.name.iter().copied())104 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))105 .collect::<String>()106 }107108 109 fn symbol(&self) -> String {110 String::from_utf8_lossy(&self.token_prefix).into()111 }112113 114 fn total_supply(&self) -> Result<U256> {115 self.consume_store_reads(1)?;116 Ok(<TotalSupply<T>>::get((self.id, self.1)).into())117 }118119 120 fn decimals(&self) -> Result<u8> {121 122 Ok(0)123 }124125 126 127 128 fn balance_of(&self, owner: Address) -> Result<U256> {129 self.consume_store_reads(1)?;130 let owner = T::CrossAccountId::from_eth(owner);131 let balance = <Balance<T>>::get((self.id, self.1, owner));132 Ok(balance.into())133 }134135 136 137 138 #[weight(<CommonWeights<T>>::transfer())]139 fn transfer(&mut self, caller: Caller, to: Address, amount: U256) -> Result<bool> {140 let caller = T::CrossAccountId::from_eth(caller);141 let to = T::CrossAccountId::from_eth(to);142 let amount = amount.try_into().map_err(|_| "amount overflow")?;143 let budget = self144 .recorder145 .weight_calls_budget(<StructureWeight<T>>::find_parent());146147 <Pallet<T>>::transfer(self, &caller, &to, self.1, amount, &budget)148 .map_err(dispatch_to_evm::<T>)?;149 Ok(true)150 }151152 153 154 155 156 #[weight(<CommonWeights<T>>::transfer_from())]157 fn transfer_from(158 &mut self,159 caller: Caller,160 from: Address,161 to: Address,162 amount: U256,163 ) -> Result<bool> {164 let caller = T::CrossAccountId::from_eth(caller);165 let from = T::CrossAccountId::from_eth(from);166 let to = T::CrossAccountId::from_eth(to);167 let amount = amount.try_into().map_err(|_| "amount overflow")?;168 let budget = self169 .recorder170 .weight_calls_budget(<StructureWeight<T>>::find_parent());171172 <Pallet<T>>::transfer_from(self, &caller, &from, &to, self.1, amount, &budget)173 .map_err(dispatch_to_evm::<T>)?;174 Ok(true)175 }176177 178 179 180 181 182 183 184 #[weight(<SelfWeightOf<T>>::approve())]185 fn approve(&mut self, caller: Caller, spender: Address, amount: U256) -> Result<bool> {186 let caller = T::CrossAccountId::from_eth(caller);187 let spender = T::CrossAccountId::from_eth(spender);188 let amount = amount.try_into().map_err(|_| "amount overflow")?;189190 <Pallet<T>>::set_allowance(self, &caller, &spender, self.1, amount)191 .map_err(dispatch_to_evm::<T>)?;192 Ok(true)193 }194195 196 197 198 199 fn allowance(&self, owner: Address, spender: Address) -> Result<U256> {200 self.consume_store_reads(1)?;201 let owner = T::CrossAccountId::from_eth(owner);202 let spender = T::CrossAccountId::from_eth(spender);203204 Ok(<Allowance<T>>::get((self.id, self.1, owner, spender)).into())205 }206}207208#[solidity_interface(name = ERC20UniqueExtensions, enum(derive(PreDispatch)), enum_attr(weight))]209impl<T: Config> RefungibleTokenHandle<T>210where211 T::AccountId: From<[u8; 32]>,212{213 214 215 216 217 fn allowance_cross(&self, owner: CrossAddress, spender: CrossAddress) -> Result<U256> {218 let owner = owner.into_sub_cross_account::<T>()?;219 let spender = spender.into_sub_cross_account::<T>()?;220221 Ok(<Allowance<T>>::get((self.id, self.1, owner, spender)).into())222 }223224 225 226 227 228 #[weight(<SelfWeightOf<T>>::burn_from())]229 #[solidity(hide)]230 fn burn_from(&mut self, caller: Caller, from: Address, amount: U256) -> Result<bool> {231 let caller = T::CrossAccountId::from_eth(caller);232 let from = T::CrossAccountId::from_eth(from);233 let amount = amount.try_into().map_err(|_| "amount overflow")?;234 let budget = self235 .recorder236 .weight_calls_budget(<StructureWeight<T>>::find_parent());237238 <Pallet<T>>::burn_from(self, &caller, &from, self.1, amount, &budget)239 .map_err(dispatch_to_evm::<T>)?;240 Ok(true)241 }242243 244 245 246 247 #[weight(<SelfWeightOf<T>>::burn_from())]248 fn burn_from_cross(249 &mut self,250 caller: Caller,251 from: CrossAddress,252 amount: U256,253 ) -> Result<bool> {254 let caller = T::CrossAccountId::from_eth(caller);255 let from = from.into_sub_cross_account::<T>()?;256 let amount = amount.try_into().map_err(|_| "amount overflow")?;257 let budget = self258 .recorder259 .weight_calls_budget(<StructureWeight<T>>::find_parent());260261 <Pallet<T>>::burn_from(self, &caller, &from, self.1, amount, &budget)262 .map_err(dispatch_to_evm::<T>)?;263 Ok(true)264 }265266 267 268 269 270 271 272 273 #[weight(<SelfWeightOf<T>>::approve())]274 fn approve_cross(275 &mut self,276 caller: Caller,277 spender: CrossAddress,278 amount: U256,279 ) -> Result<bool> {280 let caller = T::CrossAccountId::from_eth(caller);281 let spender = spender.into_sub_cross_account::<T>()?;282 let amount = amount.try_into().map_err(|_| "amount overflow")?;283284 <Pallet<T>>::set_allowance(self, &caller, &spender, self.1, amount)285 .map_err(dispatch_to_evm::<T>)?;286 Ok(true)287 }288289 290 291 292 fn balance_of_cross(&self, owner: CrossAddress) -> Result<U256> {293 self.consume_store_reads(1)?;294 let balance = <Balance<T>>::get((self.id, self.1, owner.into_sub_cross_account::<T>()?));295 Ok(balance.into())296 }297298 299 300 301 #[weight(<SelfWeightOf<T>>::repartition_item())]302 fn repartition(&mut self, caller: Caller, amount: U256) -> Result<bool> {303 let caller = T::CrossAccountId::from_eth(caller);304 let amount = amount.try_into().map_err(|_| "amount overflow")?;305306 <Pallet<T>>::repartition(self, &caller, self.1, amount).map_err(dispatch_to_evm::<T>)?;307 Ok(true)308 }309310 311 312 313 #[weight(<CommonWeights<T>>::transfer())]314 fn transfer_cross(&mut self, caller: Caller, to: CrossAddress, amount: U256) -> Result<bool> {315 let caller = T::CrossAccountId::from_eth(caller);316 let to = to.into_sub_cross_account::<T>()?;317 let amount = amount.try_into().map_err(|_| "amount overflow")?;318 let budget = self319 .recorder320 .weight_calls_budget(<StructureWeight<T>>::find_parent());321322 <Pallet<T>>::transfer(self, &caller, &to, self.1, amount, &budget)323 .map_err(dispatch_to_evm::<T>)?;324 Ok(true)325 }326327 328 329 330 331 #[weight(<CommonWeights<T>>::transfer_from())]332 fn transfer_from_cross(333 &mut self,334 caller: Caller,335 from: CrossAddress,336 to: CrossAddress,337 amount: U256,338 ) -> Result<bool> {339 let caller = T::CrossAccountId::from_eth(caller);340 let from = from.into_sub_cross_account::<T>()?;341 let to = to.into_sub_cross_account::<T>()?;342 let amount = amount.try_into().map_err(|_| "amount overflow")?;343 let budget = self344 .recorder345 .weight_calls_budget(<StructureWeight<T>>::find_parent());346347 <Pallet<T>>::transfer_from(self, &caller, &from, &to, self.1, amount, &budget)348 .map_err(dispatch_to_evm::<T>)?;349 Ok(true)350 }351}352353impl<T: Config> RefungibleTokenHandle<T> {354 pub fn into_inner(self) -> RefungibleHandle<T> {355 self.0356 }357 pub fn common_mut(&mut self) -> &mut RefungibleHandle<T> {358 &mut self.0359 }360}361362impl<T: Config> WithRecorder<T> for RefungibleTokenHandle<T> {363 fn recorder(&self) -> &pallet_evm_coder_substrate::SubstrateRecorder<T> {364 self.0.recorder()365 }366 fn into_recorder(self) -> pallet_evm_coder_substrate::SubstrateRecorder<T> {367 self.0.into_recorder()368 }369}370371impl<T: Config> Deref for RefungibleTokenHandle<T> {372 type Target = RefungibleHandle<T>;373374 fn deref(&self) -> &Self::Target {375 &self.0376 }377}378379#[solidity_interface(380 name = UniqueRefungibleToken,381 is(ERC20, ERC20UniqueExtensions, ERC1633),382 enum(derive(PreDispatch)),383)]384impl<T: Config> RefungibleTokenHandle<T> where T::AccountId: From<[u8; 32]> {}385386generate_stubgen!(gen_impl, UniqueRefungibleTokenCall<()>, true);387generate_stubgen!(gen_iface, UniqueRefungibleTokenCall<()>, false);388389impl<T: Config> CommonEvmHandler for RefungibleTokenHandle<T>390where391 T::AccountId: From<[u8; 32]>,392{393 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueRefungibleToken.raw");394395 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {396 call::<T, UniqueRefungibleTokenCall<T>, _, _>(handle, self)397 }398}