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>198where199 T::AccountId: From<[u8; 32]>,200{201 202 203 204 205 #[weight(<SelfWeightOf<T>>::burn_from())]206 fn burn_from(&mut self, caller: caller, from: address, amount: uint256) -> Result<bool> {207 let caller = T::CrossAccountId::from_eth(caller);208 let from = T::CrossAccountId::from_eth(from);209 let amount = amount.try_into().map_err(|_| "amount overflow")?;210 let budget = self211 .recorder212 .weight_calls_budget(<StructureWeight<T>>::find_parent());213214 <Pallet<T>>::burn_from(self, &caller, &from, self.1, amount, &budget)215 .map_err(dispatch_to_evm::<T>)?;216 Ok(true)217 }218219 220 221 222 223 #[weight(<SelfWeightOf<T>>::burn_from())]224 fn burn_from_cross(225 &mut self,226 caller: caller,227 from: pallet_common::eth::CrossAccount,228 amount: uint256,229 ) -> Result<bool> {230 let caller = T::CrossAccountId::from_eth(caller);231 let from = from.into_sub_cross_account::<T>()?;232 let amount = amount.try_into().map_err(|_| "amount overflow")?;233 let budget = self234 .recorder235 .weight_calls_budget(<StructureWeight<T>>::find_parent());236237 <Pallet<T>>::burn_from(self, &caller, &from, self.1, amount, &budget)238 .map_err(dispatch_to_evm::<T>)?;239 Ok(true)240 }241242 243 244 245 246 247 248 249 #[weight(<SelfWeightOf<T>>::approve())]250 fn approve_cross(251 &mut self,252 caller: caller,253 spender: pallet_common::eth::CrossAccount,254 amount: uint256,255 ) -> Result<bool> {256 let caller = T::CrossAccountId::from_eth(caller);257 let spender = spender.into_sub_cross_account::<T>()?;258 let amount = amount.try_into().map_err(|_| "amount overflow")?;259260 <Pallet<T>>::set_allowance(self, &caller, &spender, self.1, amount)261 .map_err(dispatch_to_evm::<T>)?;262 Ok(true)263 }264 265 266 267 #[weight(<SelfWeightOf<T>>::repartition_item())]268 fn repartition(&mut self, caller: caller, amount: uint256) -> Result<bool> {269 let caller = T::CrossAccountId::from_eth(caller);270 let amount = amount.try_into().map_err(|_| "amount overflow")?;271272 <Pallet<T>>::repartition(self, &caller, self.1, amount).map_err(dispatch_to_evm::<T>)?;273 Ok(true)274 }275276 277 278 279 #[weight(<CommonWeights<T>>::transfer())]280 fn transfer_cross(281 &mut self,282 caller: caller,283 to: pallet_common::eth::CrossAccount,284 amount: uint256,285 ) -> Result<bool> {286 let caller = T::CrossAccountId::from_eth(caller);287 let to = to.into_sub_cross_account::<T>()?;288 let amount = amount.try_into().map_err(|_| "amount overflow")?;289 let budget = self290 .recorder291 .weight_calls_budget(<StructureWeight<T>>::find_parent());292293 <Pallet<T>>::transfer(self, &caller, &to, self.1, amount, &budget)294 .map_err(dispatch_to_evm::<T>)?;295 Ok(true)296 }297298 299 300 301 302 #[weight(<CommonWeights<T>>::transfer_from())]303 fn transfer_from_cross(304 &mut self,305 caller: caller,306 from: pallet_common::eth::CrossAccount,307 to: pallet_common::eth::CrossAccount,308 amount: uint256,309 ) -> Result<bool> {310 let caller = T::CrossAccountId::from_eth(caller);311 let from = from.into_sub_cross_account::<T>()?;312 let to = to.into_sub_cross_account::<T>()?;313 let amount = amount.try_into().map_err(|_| "amount overflow")?;314 let budget = self315 .recorder316 .weight_calls_budget(<StructureWeight<T>>::find_parent());317318 <Pallet<T>>::transfer_from(self, &caller, &from, &to, self.1, amount, &budget)319 .map_err(dispatch_to_evm::<T>)?;320 Ok(true)321 }322}323324impl<T: Config> RefungibleTokenHandle<T> {325 pub fn into_inner(self) -> RefungibleHandle<T> {326 self.0327 }328 pub fn common_mut(&mut self) -> &mut RefungibleHandle<T> {329 &mut self.0330 }331}332333impl<T: Config> WithRecorder<T> for RefungibleTokenHandle<T> {334 fn recorder(&self) -> &pallet_evm_coder_substrate::SubstrateRecorder<T> {335 self.0.recorder()336 }337 fn into_recorder(self) -> pallet_evm_coder_substrate::SubstrateRecorder<T> {338 self.0.into_recorder()339 }340}341342impl<T: Config> Deref for RefungibleTokenHandle<T> {343 type Target = RefungibleHandle<T>;344345 fn deref(&self) -> &Self::Target {346 &self.0347 }348}349350#[solidity_interface(351 name = UniqueRefungibleToken,352 is(ERC20, ERC20UniqueExtensions, ERC1633)353)]354impl<T: Config> RefungibleTokenHandle<T> where T::AccountId: From<[u8; 32]> {}355356generate_stubgen!(gen_impl, UniqueRefungibleTokenCall<()>, true);357generate_stubgen!(gen_iface, UniqueRefungibleTokenCall<()>, false);358359impl<T: Config> CommonEvmHandler for RefungibleTokenHandle<T>360where361 T::AccountId: From<[u8; 32]>,362{363 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueRefungibleToken.raw");364365 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {366 call::<T, UniqueRefungibleTokenCall<T>, _, _>(handle, self)367 }368}