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 sp_core::U256;41use sp_std::vec::Vec;42use up_data_structs::TokenId;4344use crate::{45 common::CommonWeights, erc::nesting_budget, weights::WeightInfo, Allowance, Balance, Config,46 Pallet, RefungibleHandle, SelfWeightOf, TotalSupply,47};4849505152pub struct RefungibleTokenHandle<T: Config>(pub RefungibleHandle<T>, pub TokenId);5354frontier_contract! {55 macro_rules! RefungibleTokenHandle_result {...}56 impl<T: Config> Contract for RefungibleTokenHandle<T> {...}57}5859#[solidity_interface(name = ERC1633, enum(derive(PreDispatch)), enum_attr(weight))]60impl<T: Config> RefungibleTokenHandle<T> {61 fn parent_token(&self) -> Address {62 collection_id_to_address(self.id)63 }6465 fn parent_token_id(&self) -> U256 {66 self.1.into()67 }68}6970#[derive(ToLog)]71pub enum ERC20Events {72 73 74 75 76 Transfer {77 #[indexed]78 from: Address,79 #[indexed]80 to: Address,81 value: U256,82 },83 84 85 Approval {86 #[indexed]87 owner: Address,88 #[indexed]89 spender: Address,90 value: U256,91 },92}939495969798#[solidity_interface(name = ERC20, events(ERC20Events), enum(derive(PreDispatch)), enum_attr(weight))]99impl<T: Config> RefungibleTokenHandle<T> {100 101 fn name(&self) -> String {102 decode_utf16(self.name.iter().copied())103 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))104 .collect::<String>()105 }106107 108 fn symbol(&self) -> String {109 String::from_utf8_lossy(&self.token_prefix).into()110 }111112 113 fn total_supply(&self) -> Result<U256> {114 self.consume_store_reads(1)?;115 Ok(<TotalSupply<T>>::get((self.id, self.1)).into())116 }117118 119 fn decimals(&self) -> Result<u8> {120 121 Ok(0)122 }123124 125 126 127 fn balance_of(&self, owner: Address) -> Result<U256> {128 self.consume_store_reads(1)?;129 let owner = T::CrossAccountId::from_eth(owner);130 let balance = <Balance<T>>::get((self.id, self.1, owner));131 Ok(balance.into())132 }133134 135 136 137 #[weight(<CommonWeights<T>>::transfer())]138 fn transfer(&mut self, caller: Caller, to: Address, amount: U256) -> Result<bool> {139 let caller = T::CrossAccountId::from_eth(caller);140 let to = T::CrossAccountId::from_eth(to);141 let amount = amount.try_into().map_err(|_| "amount overflow")?;142143 <Pallet<T>>::transfer(144 self,145 &caller,146 &to,147 self.1,148 amount,149 &nesting_budget(&self.recorder),150 )151 .map_err(dispatch_to_evm::<T>)?;152 Ok(true)153 }154155 156 157 158 159 #[weight(<CommonWeights<T>>::transfer_from())]160 fn transfer_from(161 &mut self,162 caller: Caller,163 from: Address,164 to: Address,165 amount: U256,166 ) -> Result<bool> {167 let caller = T::CrossAccountId::from_eth(caller);168 let from = T::CrossAccountId::from_eth(from);169 let to = T::CrossAccountId::from_eth(to);170 let amount = amount.try_into().map_err(|_| "amount overflow")?;171172 <Pallet<T>>::transfer_from(173 self,174 &caller,175 &from,176 &to,177 self.1,178 amount,179 &nesting_budget(&self.recorder),180 )181 .map_err(dispatch_to_evm::<T>)?;182 Ok(true)183 }184185 186 187 188 189 190 191 192 #[weight(<SelfWeightOf<T>>::approve())]193 fn approve(&mut self, caller: Caller, spender: Address, amount: U256) -> Result<bool> {194 let caller = T::CrossAccountId::from_eth(caller);195 let spender = T::CrossAccountId::from_eth(spender);196 let amount = amount.try_into().map_err(|_| "amount overflow")?;197198 <Pallet<T>>::set_allowance(self, &caller, &spender, self.1, amount)199 .map_err(dispatch_to_evm::<T>)?;200 Ok(true)201 }202203 204 205 206 207 fn allowance(&self, owner: Address, spender: Address) -> Result<U256> {208 self.consume_store_reads(1)?;209 let owner = T::CrossAccountId::from_eth(owner);210 let spender = T::CrossAccountId::from_eth(spender);211212 Ok(<Allowance<T>>::get((self.id, self.1, owner, spender)).into())213 }214}215216#[solidity_interface(name = ERC20UniqueExtensions, enum(derive(PreDispatch)), enum_attr(weight))]217impl<T: Config> RefungibleTokenHandle<T>218where219 T::AccountId: From<[u8; 32]>,220{221 222 223 224 225 fn allowance_cross(&self, owner: CrossAddress, spender: CrossAddress) -> Result<U256> {226 let owner = owner.into_sub_cross_account::<T>()?;227 let spender = spender.into_sub_cross_account::<T>()?;228229 Ok(<Allowance<T>>::get((self.id, self.1, owner, spender)).into())230 }231232 233 234 235 236 #[weight(<SelfWeightOf<T>>::burn_from())]237 #[solidity(hide)]238 fn burn_from(&mut self, caller: Caller, from: Address, amount: U256) -> Result<bool> {239 let caller = T::CrossAccountId::from_eth(caller);240 let from = T::CrossAccountId::from_eth(from);241 let amount = amount.try_into().map_err(|_| "amount overflow")?;242243 <Pallet<T>>::burn_from(244 self,245 &caller,246 &from,247 self.1,248 amount,249 &nesting_budget(&self.recorder),250 )251 .map_err(dispatch_to_evm::<T>)?;252 Ok(true)253 }254255 256 257 258 259 #[weight(<SelfWeightOf<T>>::burn_from())]260 fn burn_from_cross(261 &mut self,262 caller: Caller,263 from: CrossAddress,264 amount: U256,265 ) -> Result<bool> {266 let caller = T::CrossAccountId::from_eth(caller);267 let from = from.into_sub_cross_account::<T>()?;268 let amount = amount.try_into().map_err(|_| "amount overflow")?;269270 <Pallet<T>>::burn_from(271 self,272 &caller,273 &from,274 self.1,275 amount,276 &nesting_budget(&self.recorder),277 )278 .map_err(dispatch_to_evm::<T>)?;279 Ok(true)280 }281282 283 284 285 286 287 288 289 #[weight(<SelfWeightOf<T>>::approve())]290 fn approve_cross(291 &mut self,292 caller: Caller,293 spender: CrossAddress,294 amount: U256,295 ) -> Result<bool> {296 let caller = T::CrossAccountId::from_eth(caller);297 let spender = spender.into_sub_cross_account::<T>()?;298 let amount = amount.try_into().map_err(|_| "amount overflow")?;299300 <Pallet<T>>::set_allowance(self, &caller, &spender, self.1, amount)301 .map_err(dispatch_to_evm::<T>)?;302 Ok(true)303 }304305 306 307 308 fn balance_of_cross(&self, owner: CrossAddress) -> Result<U256> {309 self.consume_store_reads(1)?;310 let balance = <Balance<T>>::get((self.id, self.1, owner.into_sub_cross_account::<T>()?));311 Ok(balance.into())312 }313314 315 316 317 #[weight(<SelfWeightOf<T>>::repartition_item())]318 fn repartition(&mut self, caller: Caller, amount: U256) -> Result<bool> {319 let caller = T::CrossAccountId::from_eth(caller);320 let amount = amount.try_into().map_err(|_| "amount overflow")?;321322 <Pallet<T>>::repartition(self, &caller, self.1, amount).map_err(dispatch_to_evm::<T>)?;323 Ok(true)324 }325326 327 328 329 #[weight(<CommonWeights<T>>::transfer())]330 fn transfer_cross(&mut self, caller: Caller, to: CrossAddress, amount: U256) -> Result<bool> {331 let caller = T::CrossAccountId::from_eth(caller);332 let to = to.into_sub_cross_account::<T>()?;333 let amount = amount.try_into().map_err(|_| "amount overflow")?;334335 <Pallet<T>>::transfer(336 self,337 &caller,338 &to,339 self.1,340 amount,341 &nesting_budget(&self.recorder),342 )343 .map_err(dispatch_to_evm::<T>)?;344 Ok(true)345 }346347 348 349 350 351 #[weight(<CommonWeights<T>>::transfer_from())]352 fn transfer_from_cross(353 &mut self,354 caller: Caller,355 from: CrossAddress,356 to: CrossAddress,357 amount: U256,358 ) -> Result<bool> {359 let caller = T::CrossAccountId::from_eth(caller);360 let from = from.into_sub_cross_account::<T>()?;361 let to = to.into_sub_cross_account::<T>()?;362 let amount = amount.try_into().map_err(|_| "amount overflow")?;363364 <Pallet<T>>::transfer_from(365 self,366 &caller,367 &from,368 &to,369 self.1,370 amount,371 &nesting_budget(&self.recorder),372 )373 .map_err(dispatch_to_evm::<T>)?;374 Ok(true)375 }376}377378impl<T: Config> RefungibleTokenHandle<T> {379 pub fn into_inner(self) -> RefungibleHandle<T> {380 self.0381 }382 pub fn common_mut(&mut self) -> &mut RefungibleHandle<T> {383 &mut self.0384 }385}386387impl<T: Config> WithRecorder<T> for RefungibleTokenHandle<T> {388 fn recorder(&self) -> &pallet_evm_coder_substrate::SubstrateRecorder<T> {389 self.0.recorder()390 }391 fn into_recorder(self) -> pallet_evm_coder_substrate::SubstrateRecorder<T> {392 self.0.into_recorder()393 }394}395396impl<T: Config> Deref for RefungibleTokenHandle<T> {397 type Target = RefungibleHandle<T>;398399 fn deref(&self) -> &Self::Target {400 &self.0401 }402}403404#[solidity_interface(405 name = UniqueRefungibleToken,406 is(ERC20, ERC20UniqueExtensions, ERC1633),407 enum(derive(PreDispatch)),408)]409impl<T: Config> RefungibleTokenHandle<T> where T::AccountId: From<[u8; 32]> {}410411generate_stubgen!(gen_impl, UniqueRefungibleTokenCall<()>, true);412generate_stubgen!(gen_iface, UniqueRefungibleTokenCall<()>, false);413414impl<T: Config> CommonEvmHandler for RefungibleTokenHandle<T>415where416 T::AccountId: From<[u8; 32]>,417{418 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueRefungibleToken.raw");419420 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {421 call::<T, UniqueRefungibleTokenCall<T>, _, _>(handle, self)422 }423}