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, solidity, types::*,29 weight,30};31use pallet_common::{32 CommonWeightInfo,33 erc::{CommonEvmHandler, PrecompileResult},34 eth::collection_id_to_address,35};36use pallet_evm::{account::CrossAccountId, PrecompileHandle};37use pallet_evm_coder_substrate::{call, dispatch_to_evm, WithRecorder};38use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};39use sp_std::vec::Vec;40use up_data_structs::TokenId;4142use crate::{43 Allowance, Balance, common::CommonWeights, Config, Pallet, RefungibleHandle, SelfWeightOf,44 TotalSupply, weights::WeightInfo,45};4647484950pub struct RefungibleTokenHandle<T: Config>(pub RefungibleHandle<T>, pub TokenId);5152#[solidity_interface(name = ERC1633)]53impl<T: Config> RefungibleTokenHandle<T> {54 fn parent_token(&self) -> Result<address> {55 Ok(collection_id_to_address(self.id))56 }5758 fn parent_token_id(&self) -> Result<uint256> {59 Ok(self.1.into())60 }61}6263#[derive(ToLog)]64pub enum ERC20Events {65 66 67 68 69 Transfer {70 #[indexed]71 from: address,72 #[indexed]73 to: address,74 value: uint256,75 },76 77 78 Approval {79 #[indexed]80 owner: address,81 #[indexed]82 spender: address,83 value: uint256,84 },85}868788899091#[solidity_interface(name = ERC20, events(ERC20Events))]92impl<T: Config> RefungibleTokenHandle<T> {93 94 fn name(&self) -> Result<string> {95 Ok(decode_utf16(self.name.iter().copied())96 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))97 .collect::<string>())98 }99100 101 fn symbol(&self) -> Result<string> {102 Ok(string::from_utf8_lossy(&self.token_prefix).into())103 }104105 106 fn total_supply(&self) -> Result<uint256> {107 self.consume_store_reads(1)?;108 Ok(<TotalSupply<T>>::get((self.id, self.1)).into())109 }110111 112 fn decimals(&self) -> Result<uint8> {113 114 Ok(0)115 }116117 118 119 120 fn balance_of(&self, owner: address) -> Result<uint256> {121 self.consume_store_reads(1)?;122 let owner = T::CrossAccountId::from_eth(owner);123 let balance = <Balance<T>>::get((self.id, self.1, owner));124 Ok(balance.into())125 }126127 128 129 130 #[weight(<CommonWeights<T>>::transfer())]131 fn transfer(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {132 let caller = T::CrossAccountId::from_eth(caller);133 let to = T::CrossAccountId::from_eth(to);134 let amount = amount.try_into().map_err(|_| "amount overflow")?;135 let budget = self136 .recorder137 .weight_calls_budget(<StructureWeight<T>>::find_parent());138139 <Pallet<T>>::transfer(self, &caller, &to, self.1, amount, &budget)140 .map_err(dispatch_to_evm::<T>)?;141 Ok(true)142 }143144 145 146 147 148 #[weight(<CommonWeights<T>>::transfer_from())]149 fn transfer_from(150 &mut self,151 caller: caller,152 from: address,153 to: address,154 amount: uint256,155 ) -> Result<bool> {156 let caller = T::CrossAccountId::from_eth(caller);157 let from = T::CrossAccountId::from_eth(from);158 let to = T::CrossAccountId::from_eth(to);159 let amount = amount.try_into().map_err(|_| "amount overflow")?;160 let budget = self161 .recorder162 .weight_calls_budget(<StructureWeight<T>>::find_parent());163164 <Pallet<T>>::transfer_from(self, &caller, &from, &to, self.1, amount, &budget)165 .map_err(dispatch_to_evm::<T>)?;166 Ok(true)167 }168169 170 171 172 173 174 175 176 #[weight(<SelfWeightOf<T>>::approve())]177 fn approve(&mut self, caller: caller, spender: address, amount: uint256) -> Result<bool> {178 let caller = T::CrossAccountId::from_eth(caller);179 let spender = T::CrossAccountId::from_eth(spender);180 let amount = amount.try_into().map_err(|_| "amount overflow")?;181182 <Pallet<T>>::set_allowance(self, &caller, &spender, self.1, amount)183 .map_err(dispatch_to_evm::<T>)?;184 Ok(true)185 }186187 188 189 190 191 fn allowance(&self, owner: address, spender: address) -> Result<uint256> {192 self.consume_store_reads(1)?;193 let owner = T::CrossAccountId::from_eth(owner);194 let spender = T::CrossAccountId::from_eth(spender);195196 Ok(<Allowance<T>>::get((self.id, self.1, owner, spender)).into())197 }198}199200#[solidity_interface(name = ERC20UniqueExtensions)]201impl<T: Config> RefungibleTokenHandle<T>202where203 T::AccountId: From<[u8; 32]>,204{205 206 207 208 209 #[weight(<SelfWeightOf<T>>::burn_from())]210 #[solidity(hide)]211 fn burn_from(&mut self, caller: caller, from: address, amount: uint256) -> Result<bool> {212 let caller = T::CrossAccountId::from_eth(caller);213 let from = T::CrossAccountId::from_eth(from);214 let amount = amount.try_into().map_err(|_| "amount overflow")?;215 let budget = self216 .recorder217 .weight_calls_budget(<StructureWeight<T>>::find_parent());218219 <Pallet<T>>::burn_from(self, &caller, &from, self.1, amount, &budget)220 .map_err(dispatch_to_evm::<T>)?;221 Ok(true)222 }223224 225 226 227 228 #[weight(<SelfWeightOf<T>>::burn_from())]229 fn burn_from_cross(230 &mut self,231 caller: caller,232 from: pallet_common::eth::CrossAddress,233 amount: uint256,234 ) -> Result<bool> {235 let caller = T::CrossAccountId::from_eth(caller);236 let from = from.into_sub_cross_account::<T>()?;237 let amount = amount.try_into().map_err(|_| "amount overflow")?;238 let budget = self239 .recorder240 .weight_calls_budget(<StructureWeight<T>>::find_parent());241242 <Pallet<T>>::burn_from(self, &caller, &from, self.1, amount, &budget)243 .map_err(dispatch_to_evm::<T>)?;244 Ok(true)245 }246247 248 249 250 251 252 253 254 #[weight(<SelfWeightOf<T>>::approve())]255 fn approve_cross(256 &mut self,257 caller: caller,258 spender: pallet_common::eth::CrossAddress,259 amount: uint256,260 ) -> Result<bool> {261 let caller = T::CrossAccountId::from_eth(caller);262 let spender = spender.into_sub_cross_account::<T>()?;263 let amount = amount.try_into().map_err(|_| "amount overflow")?;264265 <Pallet<T>>::set_allowance(self, &caller, &spender, self.1, amount)266 .map_err(dispatch_to_evm::<T>)?;267 Ok(true)268 }269 270 271 272 #[weight(<SelfWeightOf<T>>::repartition_item())]273 fn repartition(&mut self, caller: caller, amount: uint256) -> Result<bool> {274 let caller = T::CrossAccountId::from_eth(caller);275 let amount = amount.try_into().map_err(|_| "amount overflow")?;276277 <Pallet<T>>::repartition(self, &caller, self.1, amount).map_err(dispatch_to_evm::<T>)?;278 Ok(true)279 }280281 282 283 284 #[weight(<CommonWeights<T>>::transfer())]285 fn transfer_cross(286 &mut self,287 caller: caller,288 to: pallet_common::eth::CrossAddress,289 amount: uint256,290 ) -> Result<bool> {291 let caller = T::CrossAccountId::from_eth(caller);292 let to = to.into_sub_cross_account::<T>()?;293 let amount = amount.try_into().map_err(|_| "amount overflow")?;294 let budget = self295 .recorder296 .weight_calls_budget(<StructureWeight<T>>::find_parent());297298 <Pallet<T>>::transfer(self, &caller, &to, self.1, amount, &budget)299 .map_err(dispatch_to_evm::<T>)?;300 Ok(true)301 }302303 304 305 306 307 #[weight(<CommonWeights<T>>::transfer_from())]308 fn transfer_from_cross(309 &mut self,310 caller: caller,311 from: pallet_common::eth::CrossAddress,312 to: pallet_common::eth::CrossAddress,313 amount: uint256,314 ) -> Result<bool> {315 let caller = T::CrossAccountId::from_eth(caller);316 let from = from.into_sub_cross_account::<T>()?;317 let to = to.into_sub_cross_account::<T>()?;318 let amount = amount.try_into().map_err(|_| "amount overflow")?;319 let budget = self320 .recorder321 .weight_calls_budget(<StructureWeight<T>>::find_parent());322323 <Pallet<T>>::transfer_from(self, &caller, &from, &to, self.1, amount, &budget)324 .map_err(dispatch_to_evm::<T>)?;325 Ok(true)326 }327}328329impl<T: Config> RefungibleTokenHandle<T> {330 pub fn into_inner(self) -> RefungibleHandle<T> {331 self.0332 }333 pub fn common_mut(&mut self) -> &mut RefungibleHandle<T> {334 &mut self.0335 }336}337338impl<T: Config> WithRecorder<T> for RefungibleTokenHandle<T> {339 fn recorder(&self) -> &pallet_evm_coder_substrate::SubstrateRecorder<T> {340 self.0.recorder()341 }342 fn into_recorder(self) -> pallet_evm_coder_substrate::SubstrateRecorder<T> {343 self.0.into_recorder()344 }345}346347impl<T: Config> Deref for RefungibleTokenHandle<T> {348 type Target = RefungibleHandle<T>;349350 fn deref(&self) -> &Self::Target {351 &self.0352 }353}354355#[solidity_interface(356 name = UniqueRefungibleToken,357 is(ERC20, ERC20UniqueExtensions, ERC1633)358)]359impl<T: Config> RefungibleTokenHandle<T> where T::AccountId: From<[u8; 32]> {}360361generate_stubgen!(gen_impl, UniqueRefungibleTokenCall<()>, true);362generate_stubgen!(gen_iface, UniqueRefungibleTokenCall<()>, false);363364impl<T: Config> CommonEvmHandler for RefungibleTokenHandle<T>365where366 T::AccountId: From<[u8; 32]>,367{368 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueRefungibleToken.raw");369370 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {371 call::<T, UniqueRefungibleTokenCall<T>, _, _>(handle, self)372 }373}