12345678910111213141516171819202122use core::{23 char::{REPLACEMENT_CHARACTER, decode_utf16},24 convert::TryInto,25 ops::Deref,26};27use evm_coder::{abi::AbiType, ToLog, generate_stubgen, solidity_interface, types::*};28use pallet_common::{29 erc::{CommonEvmHandler, PrecompileResult},30 eth::{collection_id_to_address, CrossAddress},31 CommonWeightInfo,32};33use pallet_evm::{account::CrossAccountId, PrecompileHandle};34use pallet_evm_coder_substrate::{35 call, dispatch_to_evm, WithRecorder, frontier_contract,36 execution::{Result, PreDispatch},37};38use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};39use sp_std::vec::Vec;40use sp_core::U256;41use up_data_structs::TokenId;4243use crate::{Allowance, Balance, Config, Pallet, RefungibleHandle, TotalSupply, common::CommonWeights, SelfWeightOf, weights::WeightInfo};4445464748pub struct RefungibleTokenHandle<T: Config>(pub RefungibleHandle<T>, pub TokenId);4950frontier_contract! {51 macro_rules! RefungibleTokenHandle_result {...}52 impl<T: Config> Contract for RefungibleTokenHandle<T> {...}53}5455#[solidity_interface(name = ERC1633, enum(derive(PreDispatch)), enum_attr(weight))]56impl<T: Config> RefungibleTokenHandle<T> {57 fn parent_token(&self) -> Address {58 collection_id_to_address(self.id)59 }6061 fn parent_token_id(&self) -> U256 {62 self.1.into()63 }64}6566#[derive(ToLog)]67pub enum ERC20Events {68 69 70 71 72 Transfer {73 #[indexed]74 from: Address,75 #[indexed]76 to: Address,77 value: U256,78 },79 80 81 Approval {82 #[indexed]83 owner: Address,84 #[indexed]85 spender: Address,86 value: U256,87 },88}899091929394#[solidity_interface(name = ERC20, events(ERC20Events), enum(derive(PreDispatch)), enum_attr(weight))]95impl<T: Config> RefungibleTokenHandle<T> {96 97 fn name(&self) -> String {98 decode_utf16(self.name.iter().copied())99 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))100 .collect::<String>()101 }102103 104 fn symbol(&self) -> String {105 String::from_utf8_lossy(&self.token_prefix).into()106 }107108 109 fn total_supply(&self) -> Result<U256> {110 self.consume_store_reads(1)?;111 Ok(<TotalSupply<T>>::get((self.id, self.1)).into())112 }113114 115 fn decimals(&self) -> Result<u8> {116 117 Ok(0)118 }119120 121 122 123 fn balance_of(&self, owner: Address) -> Result<U256> {124 self.consume_store_reads(1)?;125 let owner = T::CrossAccountId::from_eth(owner);126 let balance = <Balance<T>>::get((self.id, self.1, owner));127 Ok(balance.into())128 }129130 131 132 133 #[weight(<CommonWeights<T>>::transfer())]134 fn transfer(&mut self, caller: Caller, to: Address, amount: U256) -> Result<bool> {135 let caller = T::CrossAccountId::from_eth(caller);136 let to = T::CrossAccountId::from_eth(to);137 let amount = amount.try_into().map_err(|_| "amount overflow")?;138 let budget = self139 .recorder140 .weight_calls_budget(<StructureWeight<T>>::find_parent());141142 <Pallet<T>>::transfer(self, &caller, &to, self.1, amount, &budget)143 .map_err(dispatch_to_evm::<T>)?;144 Ok(true)145 }146147 148 149 150 151 #[weight(<CommonWeights<T>>::transfer_from())]152 fn transfer_from(153 &mut self,154 caller: Caller,155 from: Address,156 to: Address,157 amount: U256,158 ) -> Result<bool> {159 let caller = T::CrossAccountId::from_eth(caller);160 let from = T::CrossAccountId::from_eth(from);161 let to = T::CrossAccountId::from_eth(to);162 let amount = amount.try_into().map_err(|_| "amount overflow")?;163 let budget = self164 .recorder165 .weight_calls_budget(<StructureWeight<T>>::find_parent());166167 <Pallet<T>>::transfer_from(self, &caller, &from, &to, self.1, amount, &budget)168 .map_err(dispatch_to_evm::<T>)?;169 Ok(true)170 }171172 173 174 175 176 177 178 179 #[weight(<SelfWeightOf<T>>::approve())]180 fn approve(&mut self, caller: Caller, spender: Address, amount: U256) -> Result<bool> {181 let caller = T::CrossAccountId::from_eth(caller);182 let spender = T::CrossAccountId::from_eth(spender);183 let amount = amount.try_into().map_err(|_| "amount overflow")?;184185 <Pallet<T>>::set_allowance(self, &caller, &spender, self.1, amount)186 .map_err(dispatch_to_evm::<T>)?;187 Ok(true)188 }189190 191 192 193 194 fn allowance(&self, owner: Address, spender: Address) -> Result<U256> {195 self.consume_store_reads(1)?;196 let owner = T::CrossAccountId::from_eth(owner);197 let spender = T::CrossAccountId::from_eth(spender);198199 Ok(<Allowance<T>>::get((self.id, self.1, owner, spender)).into())200 }201}202203#[solidity_interface(name = ERC20UniqueExtensions, enum(derive(PreDispatch)), enum_attr(weight))]204impl<T: Config> RefungibleTokenHandle<T>205where206 T::AccountId: From<[u8; 32]>,207{208 209 210 211 212 fn allowance_cross(&self, owner: CrossAddress, spender: CrossAddress) -> Result<U256> {213 let owner = owner.into_sub_cross_account::<T>()?;214 let spender = spender.into_sub_cross_account::<T>()?;215216 Ok(<Allowance<T>>::get((self.id, self.1, owner, spender)).into())217 }218219 220 221 222 223 #[weight(<SelfWeightOf<T>>::burn_from())]224 #[solidity(hide)]225 fn burn_from(&mut self, caller: Caller, from: Address, amount: U256) -> Result<bool> {226 let caller = T::CrossAccountId::from_eth(caller);227 let from = T::CrossAccountId::from_eth(from);228 let amount = amount.try_into().map_err(|_| "amount overflow")?;229 let budget = self230 .recorder231 .weight_calls_budget(<StructureWeight<T>>::find_parent());232233 <Pallet<T>>::burn_from(self, &caller, &from, self.1, amount, &budget)234 .map_err(dispatch_to_evm::<T>)?;235 Ok(true)236 }237238 239 240 241 242 #[weight(<SelfWeightOf<T>>::burn_from())]243 fn burn_from_cross(244 &mut self,245 caller: Caller,246 from: CrossAddress,247 amount: U256,248 ) -> Result<bool> {249 let caller = T::CrossAccountId::from_eth(caller);250 let from = from.into_sub_cross_account::<T>()?;251 let amount = amount.try_into().map_err(|_| "amount overflow")?;252 let budget = self253 .recorder254 .weight_calls_budget(<StructureWeight<T>>::find_parent());255256 <Pallet<T>>::burn_from(self, &caller, &from, self.1, amount, &budget)257 .map_err(dispatch_to_evm::<T>)?;258 Ok(true)259 }260261 262 263 264 265 266 267 268 #[weight(<SelfWeightOf<T>>::approve())]269 fn approve_cross(270 &mut self,271 caller: Caller,272 spender: CrossAddress,273 amount: U256,274 ) -> Result<bool> {275 let caller = T::CrossAccountId::from_eth(caller);276 let spender = spender.into_sub_cross_account::<T>()?;277 let amount = amount.try_into().map_err(|_| "amount overflow")?;278279 <Pallet<T>>::set_allowance(self, &caller, &spender, self.1, amount)280 .map_err(dispatch_to_evm::<T>)?;281 Ok(true)282 }283 284 285 286 #[weight(<SelfWeightOf<T>>::repartition_item())]287 fn repartition(&mut self, caller: Caller, amount: U256) -> Result<bool> {288 let caller = T::CrossAccountId::from_eth(caller);289 let amount = amount.try_into().map_err(|_| "amount overflow")?;290291 <Pallet<T>>::repartition(self, &caller, self.1, amount).map_err(dispatch_to_evm::<T>)?;292 Ok(true)293 }294295 296 297 298 #[weight(<CommonWeights<T>>::transfer())]299 fn transfer_cross(&mut self, caller: Caller, to: CrossAddress, amount: U256) -> Result<bool> {300 let caller = T::CrossAccountId::from_eth(caller);301 let to = to.into_sub_cross_account::<T>()?;302 let amount = amount.try_into().map_err(|_| "amount overflow")?;303 let budget = self304 .recorder305 .weight_calls_budget(<StructureWeight<T>>::find_parent());306307 <Pallet<T>>::transfer(self, &caller, &to, self.1, amount, &budget)308 .map_err(dispatch_to_evm::<T>)?;309 Ok(true)310 }311312 313 314 315 316 #[weight(<CommonWeights<T>>::transfer_from())]317 fn transfer_from_cross(318 &mut self,319 caller: Caller,320 from: CrossAddress,321 to: CrossAddress,322 amount: U256,323 ) -> Result<bool> {324 let caller = T::CrossAccountId::from_eth(caller);325 let from = from.into_sub_cross_account::<T>()?;326 let to = to.into_sub_cross_account::<T>()?;327 let amount = amount.try_into().map_err(|_| "amount overflow")?;328 let budget = self329 .recorder330 .weight_calls_budget(<StructureWeight<T>>::find_parent());331332 <Pallet<T>>::transfer_from(self, &caller, &from, &to, self.1, amount, &budget)333 .map_err(dispatch_to_evm::<T>)?;334 Ok(true)335 }336}337338impl<T: Config> RefungibleTokenHandle<T> {339 pub fn into_inner(self) -> RefungibleHandle<T> {340 self.0341 }342 pub fn common_mut(&mut self) -> &mut RefungibleHandle<T> {343 &mut self.0344 }345}346347impl<T: Config> WithRecorder<T> for RefungibleTokenHandle<T> {348 fn recorder(&self) -> &pallet_evm_coder_substrate::SubstrateRecorder<T> {349 self.0.recorder()350 }351 fn into_recorder(self) -> pallet_evm_coder_substrate::SubstrateRecorder<T> {352 self.0.into_recorder()353 }354}355356impl<T: Config> Deref for RefungibleTokenHandle<T> {357 type Target = RefungibleHandle<T>;358359 fn deref(&self) -> &Self::Target {360 &self.0361 }362}363364#[solidity_interface(365 name = UniqueRefungibleToken,366 is(ERC20, ERC20UniqueExtensions, ERC1633),367 enum(derive(PreDispatch)),368)]369impl<T: Config> RefungibleTokenHandle<T> where T::AccountId: From<[u8; 32]> {}370371generate_stubgen!(gen_impl, UniqueRefungibleTokenCall<()>, true);372generate_stubgen!(gen_iface, UniqueRefungibleTokenCall<()>, false);373374impl<T: Config> CommonEvmHandler for RefungibleTokenHandle<T>375where376 T::AccountId: From<[u8; 32]>,377{378 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueRefungibleToken.raw");379380 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {381 call::<T, UniqueRefungibleTokenCall<T>, _, _>(handle, self)382 }383}