12345678910111213141516171819202122use core::{23 char::{REPLACEMENT_CHARACTER, decode_utf16},24 convert::TryInto,25 ops::Deref,26};27use evm_coder::{28 abi::AbiType, ToLog, execution::*, generate_stubgen, solidity_interface, types::*, weight,29};30use pallet_common::{31 CommonWeightInfo,32 erc::{CommonEvmHandler, PrecompileResult},33 eth::collection_id_to_address,34};35use pallet_evm::{account::CrossAccountId, PrecompileHandle};36use pallet_evm_coder_substrate::{call, dispatch_to_evm, WithRecorder};37use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};38use sp_std::vec::Vec;39use up_data_structs::TokenId;4041use crate::{42 Allowance, Balance, common::CommonWeights, Config, Pallet, RefungibleHandle, SelfWeightOf,43 TotalSupply, weights::WeightInfo,44};4546pub struct RefungibleTokenHandle<T: Config>(pub RefungibleHandle<T>, pub TokenId);4748#[solidity_interface(name = ERC1633)]49impl<T: Config> RefungibleTokenHandle<T> {50 fn parent_token(&self) -> Result<address> {51 Ok(collection_id_to_address(self.id))52 }5354 fn parent_token_id(&self) -> Result<uint256> {55 Ok(self.1.into())56 }57}5859#[derive(ToLog)]60pub enum ERC20Events {61 62 63 64 65 Transfer {66 #[indexed]67 from: address,68 #[indexed]69 to: address,70 value: uint256,71 },72 73 74 Approval {75 #[indexed]76 owner: address,77 #[indexed]78 spender: address,79 value: uint256,80 },81}828384858687#[solidity_interface(name = ERC20, events(ERC20Events))]88impl<T: Config> RefungibleTokenHandle<T> {89 90 fn name(&self) -> Result<string> {91 Ok(decode_utf16(self.name.iter().copied())92 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))93 .collect::<string>())94 }9596 97 fn symbol(&self) -> Result<string> {98 Ok(string::from_utf8_lossy(&self.token_prefix).into())99 }100101 102 fn total_supply(&self) -> Result<uint256> {103 self.consume_store_reads(1)?;104 Ok(<TotalSupply<T>>::get((self.id, self.1)).into())105 }106107 108 fn decimals(&self) -> Result<uint8> {109 110 Ok(0)111 }112113 114 115 116 fn balance_of(&self, owner: address) -> Result<uint256> {117 self.consume_store_reads(1)?;118 let owner = T::CrossAccountId::from_eth(owner);119 let balance = <Balance<T>>::get((self.id, self.1, owner));120 Ok(balance.into())121 }122123 124 125 126 #[weight(<CommonWeights<T>>::transfer())]127 fn transfer(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {128 let caller = T::CrossAccountId::from_eth(caller);129 let to = T::CrossAccountId::from_eth(to);130 let amount = amount.try_into().map_err(|_| "amount overflow")?;131 let budget = self132 .recorder133 .weight_calls_budget(<StructureWeight<T>>::find_parent());134135 <Pallet<T>>::transfer(self, &caller, &to, self.1, amount, &budget)136 .map_err(dispatch_to_evm::<T>)?;137 Ok(true)138 }139140 141 142 143 144 #[weight(<CommonWeights<T>>::transfer_from())]145 fn transfer_from(146 &mut self,147 caller: caller,148 from: address,149 to: address,150 amount: uint256,151 ) -> Result<bool> {152 let caller = T::CrossAccountId::from_eth(caller);153 let from = T::CrossAccountId::from_eth(from);154 let to = T::CrossAccountId::from_eth(to);155 let amount = amount.try_into().map_err(|_| "amount overflow")?;156 let budget = self157 .recorder158 .weight_calls_budget(<StructureWeight<T>>::find_parent());159160 <Pallet<T>>::transfer_from(self, &caller, &from, &to, self.1, amount, &budget)161 .map_err(dispatch_to_evm::<T>)?;162 Ok(true)163 }164165 166 167 168 169 170 171 172 #[weight(<SelfWeightOf<T>>::approve())]173 fn approve(&mut self, caller: caller, spender: address, amount: uint256) -> Result<bool> {174 let caller = T::CrossAccountId::from_eth(caller);175 let spender = T::CrossAccountId::from_eth(spender);176 let amount = amount.try_into().map_err(|_| "amount overflow")?;177178 <Pallet<T>>::set_allowance(self, &caller, &spender, self.1, amount)179 .map_err(dispatch_to_evm::<T>)?;180 Ok(true)181 }182183 184 185 186 187 fn allowance(&self, owner: address, spender: address) -> Result<uint256> {188 self.consume_store_reads(1)?;189 let owner = T::CrossAccountId::from_eth(owner);190 let spender = T::CrossAccountId::from_eth(spender);191192 Ok(<Allowance<T>>::get((self.id, self.1, owner, spender)).into())193 }194}195196#[solidity_interface(name = ERC20UniqueExtensions)]197impl<T: Config> RefungibleTokenHandle<T> {198 199 200 201 202 #[weight(<SelfWeightOf<T>>::burn_from())]203 fn burn_from(&mut self, caller: caller, from: address, amount: uint256) -> Result<bool> {204 let caller = T::CrossAccountId::from_eth(caller);205 let from = T::CrossAccountId::from_eth(from);206 let amount = amount.try_into().map_err(|_| "amount overflow")?;207 let budget = self208 .recorder209 .weight_calls_budget(<StructureWeight<T>>::find_parent());210211 <Pallet<T>>::burn_from(self, &caller, &from, self.1, amount, &budget)212 .map_err(dispatch_to_evm::<T>)?;213 Ok(true)214 }215216 217 218 219 #[weight(<SelfWeightOf<T>>::repartition_item())]220 fn repartition(&mut self, caller: caller, amount: uint256) -> Result<bool> {221 let caller = T::CrossAccountId::from_eth(caller);222 let amount = amount.try_into().map_err(|_| "amount overflow")?;223224 <Pallet<T>>::repartition(self, &caller, self.1, amount).map_err(dispatch_to_evm::<T>)?;225 Ok(true)226 }227}228229impl<T: Config> RefungibleTokenHandle<T> {230 pub fn into_inner(self) -> RefungibleHandle<T> {231 self.0232 }233 pub fn common_mut(&mut self) -> &mut RefungibleHandle<T> {234 &mut self.0235 }236}237238impl<T: Config> WithRecorder<T> for RefungibleTokenHandle<T> {239 fn recorder(&self) -> &pallet_evm_coder_substrate::SubstrateRecorder<T> {240 self.0.recorder()241 }242 fn into_recorder(self) -> pallet_evm_coder_substrate::SubstrateRecorder<T> {243 self.0.into_recorder()244 }245}246247impl<T: Config> Deref for RefungibleTokenHandle<T> {248 type Target = RefungibleHandle<T>;249250 fn deref(&self) -> &Self::Target {251 &self.0252 }253}254255#[solidity_interface(256 name = UniqueRefungibleToken,257 is(ERC20, ERC20UniqueExtensions, ERC1633)258)]259impl<T: Config> RefungibleTokenHandle<T> where T::AccountId: From<[u8; 32]> {}260261generate_stubgen!(gen_impl, UniqueRefungibleTokenCall<()>, true);262generate_stubgen!(gen_iface, UniqueRefungibleTokenCall<()>, false);263264impl<T: Config> CommonEvmHandler for RefungibleTokenHandle<T>265where266 T::AccountId: From<[u8; 32]>,267{268 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueRefungibleToken.raw");269270 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {271 call::<T, UniqueRefungibleTokenCall<T>, _, _>(handle, self)272 }273}