1use core::{2 char::{REPLACEMENT_CHARACTER, decode_utf16},3 convert::TryInto,4};5use evm_coder::{ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*};6use frame_support::BoundedVec;7use nft_data_structs::TokenId;8use pallet_evm_coder_substrate::dispatch_to_evm;9use sp_core::{H160, U256};10use sp_std::{vec::Vec, vec};11use pallet_common::{account::CrossAccountId, erc::CommonEvmHandler};12use pallet_evm_coder_substrate::call_internal;13use pallet_common::erc::PrecompileOutput;1415use crate::{16 AccountBalance, Config, CreateItemData, DataKind, NonfungibleHandle, Owner, Pallet, TokenData,17 TokensMinted,18};1920#[derive(ToLog)]21pub enum ERC721Events {22 Transfer {23 #[indexed]24 from: address,25 #[indexed]26 to: address,27 #[indexed]28 token_id: uint256,29 },30 Approval {31 #[indexed]32 owner: address,33 #[indexed]34 approved: address,35 #[indexed]36 token_id: uint256,37 },38 #[allow(dead_code)]39 ApprovalForAll {40 #[indexed]41 owner: address,42 #[indexed]43 operator: address,44 approved: bool,45 },46}4748#[derive(ToLog)]49pub enum ERC721MintableEvents {50 #[allow(dead_code)]51 MintingFinished {},52}5354#[solidity_interface(name = "ERC721Metadata")]55impl<T: Config> NonfungibleHandle<T> {56 fn name(&self) -> Result<string> {57 Ok(decode_utf16(self.name.iter().copied())58 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))59 .collect::<string>())60 }61 fn symbol(&self) -> Result<string> {62 Ok(string::from_utf8_lossy(&self.token_prefix).into())63 }6465 #[solidity(rename_selector = "tokenURI")]66 fn token_uri(&self, token_id: uint256) -> Result<string> {67 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;68 Ok(string::from_utf8_lossy(&<TokenData<T>>::get((69 self.id,70 token_id,71 DataKind::Constant,72 )))73 .into())74 }75}7677#[solidity_interface(name = "ERC721Enumerable")]78impl<T: Config> NonfungibleHandle<T> {79 fn token_by_index(&self, index: uint256) -> Result<uint256> {80 Ok(index)81 }8283 fn token_of_owner_by_index(&self, _owner: address, _index: uint256) -> Result<uint256> {84 85 Err("not implemented".into())86 }8788 fn total_supply(&self) -> Result<uint256> {89 Ok(<Pallet<T>>::total_supply(self).into())90 }91}9293#[solidity_interface(name = "ERC721", events(ERC721Events))]94impl<T: Config> NonfungibleHandle<T> {95 fn balance_of(&self, owner: address) -> Result<uint256> {96 let owner = T::CrossAccountId::from_eth(owner);97 let balance = <AccountBalance<T>>::get((self.id, owner.as_sub()));98 Ok(balance.into())99 }100 fn owner_of(&self, token_id: uint256) -> Result<address> {101 let token: TokenId = token_id.try_into()?;102 Ok(*<Owner<T>>::get((self.id, token)).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 = token_id.try_into()?;138139 <Pallet<T>>::transfer_from(self, &caller, &from, &to, token)140 .map_err(dispatch_to_evm::<T>)?;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 = token_id.try_into()?;154155 <Pallet<T>>::set_allowance(self, &caller, token, Some(&approved))156 .map_err(dispatch_to_evm::<T>)?;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 = "ERC721Burnable")]182impl<T: Config> NonfungibleHandle<T> {183 fn burn(&mut self, caller: caller, token_id: uint256) -> Result<void> {184 let caller = T::CrossAccountId::from_eth(caller);185 let token = token_id.try_into()?;186187 <Pallet<T>>::burn(self, &caller, token).map_err(dispatch_to_evm::<T>)?;188 Ok(())189 }190}191192#[solidity_interface(name = "ERC721Mintable", events(ERC721MintableEvents))]193impl<T: Config> NonfungibleHandle<T> {194 fn minting_finished(&self) -> Result<bool> {195 Ok(false)196 }197198 fn mint(&mut self, caller: caller, to: address, token_id: uint256) -> Result<bool> {199 let caller = T::CrossAccountId::from_eth(caller);200 let to = T::CrossAccountId::from_eth(to);201 let token_id: u32 = token_id.try_into()?;202 if <TokensMinted<T>>::get(self.id)203 .checked_add(1)204 .ok_or("item id overflow")?205 != token_id206 {207 return Err("item id should be next".into());208 }209210 <Pallet<T>>::create_item(211 self,212 &caller,213 CreateItemData {214 const_data: BoundedVec::default(),215 variable_data: BoundedVec::default(),216 owner: to,217 },218 )219 .map_err(dispatch_to_evm::<T>)?;220221 Ok(true)222 }223224 #[solidity(rename_selector = "mintWithTokenURI")]225 fn mint_with_token_uri(226 &mut self,227 caller: caller,228 to: address,229 token_id: uint256,230 token_uri: string,231 ) -> Result<bool> {232 let caller = T::CrossAccountId::from_eth(caller);233 let to = T::CrossAccountId::from_eth(to);234 let token_id: u32 = token_id.try_into().map_err(|_| "amount overflow")?;235 if <TokensMinted<T>>::get(self.id)236 .checked_add(1)237 .ok_or("item id overflow")?238 != token_id239 {240 return Err("item id should be next".into());241 }242243 <Pallet<T>>::create_item(244 self,245 &caller,246 CreateItemData {247 const_data: Vec::<u8>::from(token_uri)248 .try_into()249 .map_err(|_| "token uri is too long")?,250 variable_data: BoundedVec::default(),251 owner: to,252 },253 )254 .map_err(dispatch_to_evm::<T>)?;255 Ok(true)256 }257258 fn finish_minting(&mut self, _caller: caller) -> Result<bool> {259 Err("not implementable".into())260 }261}262263#[solidity_interface(name = "ERC721UniqueExtensions")]264impl<T: Config> NonfungibleHandle<T> {265 #[solidity(rename_selector = "transfer")]266 fn transfer_nft(267 &mut self,268 caller: caller,269 to: address,270 token_id: uint256,271 _value: value,272 ) -> Result<void> {273 let caller = T::CrossAccountId::from_eth(caller);274 let to = T::CrossAccountId::from_eth(to);275 let token = token_id.try_into()?;276277 <Pallet<T>>::transfer(self, &caller, &to, token).map_err(dispatch_to_evm::<T>)?;278 Ok(())279 }280281 fn next_token_id(&self) -> Result<uint256> {282 Ok(<TokensMinted<T>>::get(self.id)283 .checked_add(1)284 .ok_or("item id overflow")?285 .into())286 }287288 fn set_variable_metadata(289 &mut self,290 caller: caller,291 token_id: uint256,292 data: bytes,293 ) -> Result<void> {294 let caller = T::CrossAccountId::from_eth(caller);295 let token = token_id.try_into()?;296297 <Pallet<T>>::set_variable_metadata(self, &caller, token, data)298 .map_err(dispatch_to_evm::<T>)?;299 Ok(())300 }301302 fn get_variable_metadata(&self, token_id: uint256) -> Result<bytes> {303 let token: TokenId = token_id.try_into()?;304305 Ok(<TokenData<T>>::get((self.id, token, DataKind::Variable)))306 }307308 fn mint_bulk(&mut self, caller: caller, to: address, token_ids: Vec<uint256>) -> Result<bool> {309 let caller = T::CrossAccountId::from_eth(caller);310 let to = T::CrossAccountId::from_eth(to);311 let mut expected_index = <TokensMinted<T>>::get(self.id)312 .checked_add(1)313 .ok_or("item id overflow")?;314315 let total_tokens = token_ids.len();316 for id in token_ids.into_iter() {317 let id: u32 = id.try_into().map_err(|_| "token id overflow")?;318 if id != expected_index {319 return Err("item id should be next".into());320 }321 expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;322 }323 let data = (0..total_tokens)324 .map(|_| CreateItemData {325 const_data: BoundedVec::default(),326 variable_data: BoundedVec::default(),327 owner: to.clone(),328 })329 .collect();330331 <Pallet<T>>::create_multiple_items(self, &caller, data).map_err(dispatch_to_evm::<T>)?;332 Ok(true)333 }334335 #[solidity(rename_selector = "mintBulkWithTokenURI")]336 fn mint_bulk_with_token_uri(337 &mut self,338 caller: caller,339 to: address,340 tokens: Vec<(uint256, string)>,341 ) -> Result<bool> {342 let caller = T::CrossAccountId::from_eth(caller);343 let to = T::CrossAccountId::from_eth(to);344 let mut expected_index = <TokensMinted<T>>::get(self.id)345 .checked_add(1)346 .ok_or("item id overflow")?;347348 let mut data = Vec::with_capacity(tokens.len());349 for (id, token_uri) in tokens {350 let id: u32 = id.try_into().map_err(|_| "token id overflow")?;351 if id != expected_index {352 panic!("item id should be next ({}) but got {}", expected_index, id);353 }354 expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;355356 data.push(CreateItemData {357 const_data: Vec::<u8>::from(token_uri)358 .try_into()359 .map_err(|_| "token uri is too long")?,360 variable_data: vec![].try_into().unwrap(),361 owner: to.clone(),362 });363 }364365 <Pallet<T>>::create_multiple_items(self, &caller, data).map_err(dispatch_to_evm::<T>)?;366 Ok(true)367 }368}369370#[solidity_interface(371 name = "UniqueNFT",372 is(373 ERC721,374 ERC721Metadata,375 ERC721Enumerable,376 ERC721UniqueExtensions,377 ERC721Mintable,378 ERC721Burnable,379 )380)]381impl<T: Config> NonfungibleHandle<T> {}382383384generate_stubgen!(gen_impl, UniqueNFTCall, true);385generate_stubgen!(gen_iface, UniqueNFTCall, false);386387pub const CODE: &[u8] = include_bytes!("./stubs/UniqueNFT.raw");388389impl<T: Config> CommonEvmHandler for NonfungibleHandle<T> {390 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueNFT.raw");391392 fn call(mut self, source: &H160, input: &[u8], value: U256) -> Option<PrecompileOutput> {393 let result = call_internal::<UniqueNFTCall, _>(*source, &mut self, value, input);394 self.0.recorder.evm_to_precompile_output(result)395 }396}