1use core::char::{decode_utf16, REPLACEMENT_CHARACTER};2use evm_coder::{ToLog, execution::Result, solidity, solidity_interface, types::*};3use core::convert::TryInto;4use alloc::format;5use crate::{Allowances, Module, Balance, CollectionHandle, CollectionMode, Config, NftItemList};6use frame_support::storage::StorageDoubleMap;7use pallet_evm::AddressMapping;8use super::account::CrossAccountId;9use sp_std::vec::Vec;1011#[solidity_interface(name = "ERC165")]12impl<T: Config> CollectionHandle<T> {13 fn supports_interface(&self, interface_id: bytes4) -> Result<bool> {14 Ok(match self.mode {15 CollectionMode::Fungible(_) => UniqueFungibleCall::supports_interface(interface_id),16 CollectionMode::NFT => UniqueNFTCall::supports_interface(interface_id),17 _ => false,18 })19 }20}2122#[solidity_interface(name = "InlineNameSymbol")]23impl<T: Config> CollectionHandle<T> {24 fn name(&self) -> Result<string> {25 Ok(decode_utf16(self.name.iter().copied())26 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))27 .collect::<string>())28 }2930 fn symbol(&self) -> Result<string> {31 Ok(string::from_utf8_lossy(&self.token_prefix).into())32 }33}3435#[solidity_interface(name = "InlineTotalSupply")]36impl<T: Config> CollectionHandle<T> {37 fn total_supply(&self) -> Result<uint256> {38 39 Ok(0.into())40 }41}4243#[solidity_interface(name = "ERC721Metadata", inline_is(InlineNameSymbol))]44impl<T: Config> CollectionHandle<T> {45 fn token_uri(&self, token_id: uint256) -> Result<string> {46 47 Ok(format!("unique.network/{}/{}", self.id, token_id))48 }49}5051#[solidity_interface(name = "ERC721Enumerable", inline_is(InlineTotalSupply))]52impl<T: Config> CollectionHandle<T> {53 fn token_by_index(&self, index: uint256) -> Result<uint256> {54 Ok(index)55 }5657 fn token_of_owner_by_index(&self, _owner: address, _index: uint256) -> Result<uint256> {58 59 Err("not implemented".into())60 }61}6263#[derive(ToLog)]64pub enum ERC721Events {65 Transfer {66 #[indexed]67 from: address,68 #[indexed]69 to: address,70 #[indexed]71 token_id: uint256,72 },73 Approval {74 #[indexed]75 owner: address,76 #[indexed]77 approved: address,78 #[indexed]79 token_id: uint256,80 },81 #[allow(dead_code)]82 ApprovalForAll {83 #[indexed]84 owner: address,85 #[indexed]86 operator: address,87 approved: bool,88 },89}9091#[solidity_interface(name = "ERC721", is(ERC165), events(ERC721Events))]92impl<T: Config> CollectionHandle<T> {93 #[solidity(rename_selector = "balanceOf")]94 fn balance_of_nft(&self, owner: address) -> Result<uint256> {95 let owner = T::EvmAddressMapping::into_account_id(owner);96 let balance = <Balance<T>>::get(self.id, owner);97 Ok(balance.into())98 }99 fn owner_of(&self, token_id: uint256) -> Result<address> {100 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;101 let token = <NftItemList<T>>::get(self.id, token_id).ok_or("unknown token")?;102 Ok(*token.owner.as_eth())103 }104 fn safe_transfer_from_with_data(105 &mut self,106 _from: address,107 _to: address,108 _token_id: uint256,109 _data: bytes,110 _value: value,111 ) -> Result<void> {112 113 Err("not implemented".into())114 }115 fn safe_transfer_from(116 &mut self,117 _from: address,118 _to: address,119 _token_id: uint256,120 _value: value,121 ) -> Result<void> {122 123 Err("not implemented".into())124 }125126 fn transfer_from(127 &mut self,128 caller: caller,129 from: address,130 to: address,131 token_id: uint256,132 _value: value,133 ) -> Result<void> {134 let caller = T::CrossAccountId::from_eth(caller);135 let from = T::CrossAccountId::from_eth(from);136 let to = T::CrossAccountId::from_eth(to);137 let token_id = token_id.try_into().map_err(|_| "token_id overflow")?;138139 <Module<T>>::transfer_from_internal(&caller, &from, &to, self, token_id, 1)140 .map_err(|_| "transferFrom error")?;141 Ok(())142 }143144 fn approve(145 &mut self,146 caller: caller,147 approved: address,148 token_id: uint256,149 _value: value,150 ) -> Result<void> {151 let caller = T::CrossAccountId::from_eth(caller);152 let approved = T::CrossAccountId::from_eth(approved);153 let token_id = token_id.try_into().map_err(|_| "token_id overflow")?;154155 <Module<T>>::approve_internal(&caller, &approved, self, token_id, 1)156 .map_err(|_| "approve internal")?;157 Ok(())158 }159160 fn set_approval_for_all(161 &mut self,162 _caller: caller,163 _operator: address,164 _approved: bool,165 ) -> Result<void> {166 167 Err("not implemented".into())168 }169170 fn get_approved(&self, _token_id: uint256) -> Result<address> {171 172 Err("not implemented".into())173 }174175 fn is_approved_for_all(&self, _owner: address, _operator: address) -> Result<address> {176 177 Err("not implemented".into())178 }179}180181#[solidity_interface(name = "ERC721UniqueExtensions")]182impl<T: Config> CollectionHandle<T> {183 #[solidity(rename_selector = "transfer")]184 fn transfer_nft(185 &mut self,186 caller: caller,187 to: address,188 token_id: uint256,189 _value: value,190 ) -> Result<void> {191 let caller = T::CrossAccountId::from_eth(caller);192 let to = T::CrossAccountId::from_eth(to);193 let token_id = token_id.try_into().map_err(|_| "amount overflow")?;194195 <Module<T>>::transfer_internal(&caller, &to, self, token_id, 1)196 .map_err(|_| "transfer error")?;197 Ok(())198 }199}200201#[solidity_interface(202 name = "UniqueNFT",203 is(204 ERC165,205 ERC721,206 ERC721Metadata,207 ERC721Enumerable,208 ERC721UniqueExtensions209 )210)]211impl<T: Config> CollectionHandle<T> {}212213#[derive(ToLog)]214pub enum ERC20Events {215 Transfer {216 #[indexed]217 from: address,218 #[indexed]219 to: address,220 value: uint256,221 },222 Approval {223 #[indexed]224 owner: address,225 #[indexed]226 spender: address,227 value: uint256,228 },229}230231#[solidity_interface(232 name = "ERC20",233 inline_is(InlineNameSymbol, InlineTotalSupply),234 events(ERC20Events)235)]236impl<T: Config> CollectionHandle<T> {237 fn decimals(&self) -> Result<uint8> {238 Ok(if let CollectionMode::Fungible(decimals) = &self.mode {239 *decimals240 } else {241 unreachable!()242 })243 }244 fn balance_of(&self, owner: address) -> Result<uint256> {245 let owner = T::EvmAddressMapping::into_account_id(owner);246 let balance = <Balance<T>>::get(self.id, owner);247 Ok(balance.into())248 }249 fn transfer(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {250 let caller = T::CrossAccountId::from_eth(caller);251 let to = T::CrossAccountId::from_eth(to);252 let amount = amount.try_into().map_err(|_| "amount overflow")?;253254 <Module<T>>::transfer_internal(&caller, &to, self, 1, amount)255 .map_err(|_| "transfer error")?;256 Ok(true)257 }258 #[solidity(rename_selector = "transferFrom")]259 fn transfer_from_fungible(260 &mut self,261 caller: caller,262 from: address,263 to: address,264 amount: uint256,265 ) -> Result<bool> {266 let caller = T::CrossAccountId::from_eth(caller);267 let from = T::CrossAccountId::from_eth(from);268 let to = T::CrossAccountId::from_eth(to);269 let amount = amount.try_into().map_err(|_| "amount overflow")?;270271 <Module<T>>::transfer_from_internal(&caller, &from, &to, self, 1, amount)272 .map_err(|_| "transferFrom error")?;273 Ok(true)274 }275 #[solidity(rename_selector = "approve")]276 fn approve_fungible(277 &mut self,278 caller: caller,279 spender: address,280 amount: uint256,281 ) -> Result<bool> {282 let caller = T::CrossAccountId::from_eth(caller);283 let spender = T::CrossAccountId::from_eth(spender);284 let amount = amount.try_into().map_err(|_| "amount overflow")?;285286 <Module<T>>::approve_internal(&caller, &spender, self, 1, amount)287 .map_err(|_| "approve internal")?;288 Ok(true)289 }290 fn allowance(&self, owner: address, spender: address) -> Result<uint256> {291 let owner = T::CrossAccountId::from_eth(owner);292 let spender = T::CrossAccountId::from_eth(spender);293294 Ok(<Allowances<T>>::get(self.id, (1, owner.as_sub(), spender.as_sub())).into())295 }296}297298#[solidity_interface(name = "UniqueFungible", is(ERC165, ERC20))]299impl<T: Config> CollectionHandle<T> {}