git.delta.rocks / unique-network / refs/commits / b70e369d3c1a

difftreelog

source

pallets/nonfungible/src/erc.rs10.0 KiBsourcehistory
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		// TODO: Not implemetable85		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		// TODO: Not implemetable113		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		// TODO: Not implemetable123		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		// TODO: Not implemetable167		Err("not implemented".into())168	}169170	fn get_approved(&self, _token_id: uint256) -> Result<address> {171		// TODO: Not implemetable172		Err("not implemented".into())173	}174175	fn is_approved_for_all(&self, _owner: address, _operator: address) -> Result<address> {176		// TODO: Not implemetable177		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> {}382383// Not a tests, but code generators384generate_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}