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

difftreelog

source

pallets/nonfungible/src/erc.rs11.5 KiBsourcehistory
1use core::{2	char::{REPLACEMENT_CHARACTER, decode_utf16},3	convert::TryInto,4};5use evm_coder::{ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*, weight};6use frame_support::BoundedVec;7use up_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::{12	account::CrossAccountId,13	erc::{CommonEvmHandler, PrecompileResult},14};15use pallet_evm_coder_substrate::call;1617use crate::{18	AccountBalance, Config, CreateItemData, NonfungibleHandle, Pallet, TokenData, TokensMinted,19	SelfWeightOf, weights::WeightInfo,20};2122#[derive(ToLog)]23pub enum ERC721Events {24	Transfer {25		#[indexed]26		from: address,27		#[indexed]28		to: address,29		#[indexed]30		token_id: uint256,31	},32	Approval {33		#[indexed]34		owner: address,35		#[indexed]36		approved: address,37		#[indexed]38		token_id: uint256,39	},40	#[allow(dead_code)]41	ApprovalForAll {42		#[indexed]43		owner: address,44		#[indexed]45		operator: address,46		approved: bool,47	},48}4950#[derive(ToLog)]51pub enum ERC721MintableEvents {52	#[allow(dead_code)]53	MintingFinished {},54}5556#[solidity_interface(name = "ERC721Metadata")]57impl<T: Config> NonfungibleHandle<T> {58	fn name(&self) -> Result<string> {59		Ok(decode_utf16(self.name.iter().copied())60			.map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))61			.collect::<string>())62	}63	fn symbol(&self) -> Result<string> {64		Ok(string::from_utf8_lossy(&self.token_prefix).into())65	}6667	/// Returns token's const_metadata68	#[solidity(rename_selector = "tokenURI")]69	fn token_uri(&self, token_id: uint256) -> Result<string> {70		self.consume_store_reads(1)?;71		let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;72		Ok(string::from_utf8_lossy(73			&<TokenData<T>>::get((self.id, token_id))74				.ok_or("token not found")?75				.const_data,76		)77		.into())78	}79}8081#[solidity_interface(name = "ERC721Enumerable")]82impl<T: Config> NonfungibleHandle<T> {83	fn token_by_index(&self, index: uint256) -> Result<uint256> {84		Ok(index)85	}8687	/// Not implemented88	fn token_of_owner_by_index(&self, _owner: address, _index: uint256) -> Result<uint256> {89		// TODO: Not implemetable90		Err("not implemented".into())91	}9293	fn total_supply(&self) -> Result<uint256> {94		self.consume_store_reads(1)?;95		Ok(<Pallet<T>>::total_supply(self).into())96	}97}9899#[solidity_interface(name = "ERC721", events(ERC721Events))]100impl<T: Config> NonfungibleHandle<T> {101	fn balance_of(&self, owner: address) -> Result<uint256> {102		self.consume_store_reads(1)?;103		let owner = T::CrossAccountId::from_eth(owner);104		let balance = <AccountBalance<T>>::get((self.id, owner));105		Ok(balance.into())106	}107	fn owner_of(&self, token_id: uint256) -> Result<address> {108		self.consume_store_reads(1)?;109		let token: TokenId = token_id.try_into()?;110		Ok(*<TokenData<T>>::get((self.id, token))111			.ok_or("token not found")?112			.owner113			.as_eth())114	}115	/// Not implemented116	fn safe_transfer_from_with_data(117		&mut self,118		_from: address,119		_to: address,120		_token_id: uint256,121		_data: bytes,122		_value: value,123	) -> Result<void> {124		// TODO: Not implemetable125		Err("not implemented".into())126	}127	/// Not implemented128	fn safe_transfer_from(129		&mut self,130		_from: address,131		_to: address,132		_token_id: uint256,133		_value: value,134	) -> Result<void> {135		// TODO: Not implemetable136		Err("not implemented".into())137	}138139	#[weight(<SelfWeightOf<T>>::transfer_from())]140	fn transfer_from(141		&mut self,142		caller: caller,143		from: address,144		to: address,145		token_id: uint256,146		_value: value,147	) -> Result<void> {148		let caller = T::CrossAccountId::from_eth(caller);149		let from = T::CrossAccountId::from_eth(from);150		let to = T::CrossAccountId::from_eth(to);151		let token = token_id.try_into()?;152153		<Pallet<T>>::transfer_from(self, &caller, &from, &to, token)154			.map_err(dispatch_to_evm::<T>)?;155		Ok(())156	}157158	#[weight(<SelfWeightOf<T>>::approve())]159	fn approve(160		&mut self,161		caller: caller,162		approved: address,163		token_id: uint256,164		_value: value,165	) -> Result<void> {166		let caller = T::CrossAccountId::from_eth(caller);167		let approved = T::CrossAccountId::from_eth(approved);168		let token = token_id.try_into()?;169170		<Pallet<T>>::set_allowance(self, &caller, token, Some(&approved))171			.map_err(dispatch_to_evm::<T>)?;172		Ok(())173	}174175	/// Not implemented176	fn set_approval_for_all(177		&mut self,178		_caller: caller,179		_operator: address,180		_approved: bool,181	) -> Result<void> {182		// TODO: Not implemetable183		Err("not implemented".into())184	}185186	/// Not implemented187	fn get_approved(&self, _token_id: uint256) -> Result<address> {188		// TODO: Not implemetable189		Err("not implemented".into())190	}191192	/// Not implemented193	fn is_approved_for_all(&self, _owner: address, _operator: address) -> Result<address> {194		// TODO: Not implemetable195		Err("not implemented".into())196	}197}198199#[solidity_interface(name = "ERC721Burnable")]200impl<T: Config> NonfungibleHandle<T> {201	#[weight(<SelfWeightOf<T>>::burn_item())]202	fn burn(&mut self, caller: caller, token_id: uint256) -> Result<void> {203		let caller = T::CrossAccountId::from_eth(caller);204		let token = token_id.try_into()?;205206		<Pallet<T>>::burn(self, &caller, token).map_err(dispatch_to_evm::<T>)?;207		Ok(())208	}209}210211#[solidity_interface(name = "ERC721Mintable", events(ERC721MintableEvents))]212impl<T: Config> NonfungibleHandle<T> {213	fn minting_finished(&self) -> Result<bool> {214		Ok(false)215	}216217	/// `token_id` should be obtained with `next_token_id` method,218	/// unlike standard, you can't specify it manually219	#[weight(<SelfWeightOf<T>>::create_item())]220	fn mint(&mut self, caller: caller, to: address, token_id: uint256) -> Result<bool> {221		let caller = T::CrossAccountId::from_eth(caller);222		let to = T::CrossAccountId::from_eth(to);223		let token_id: u32 = token_id.try_into()?;224		if <TokensMinted<T>>::get(self.id)225			.checked_add(1)226			.ok_or("item id overflow")?227			!= token_id228		{229			return Err("item id should be next".into());230		}231232		<Pallet<T>>::create_item(233			self,234			&caller,235			CreateItemData {236				const_data: BoundedVec::default(),237				variable_data: BoundedVec::default(),238				owner: to,239			},240		)241		.map_err(dispatch_to_evm::<T>)?;242243		Ok(true)244	}245246	/// `token_id` should be obtained with `next_token_id` method,247	/// unlike standard, you can't specify it manually248	#[solidity(rename_selector = "mintWithTokenURI")]249	#[weight(<SelfWeightOf<T>>::create_item())]250	fn mint_with_token_uri(251		&mut self,252		caller: caller,253		to: address,254		token_id: uint256,255		token_uri: string,256	) -> Result<bool> {257		let caller = T::CrossAccountId::from_eth(caller);258		let to = T::CrossAccountId::from_eth(to);259		let token_id: u32 = token_id.try_into().map_err(|_| "amount overflow")?;260		if <TokensMinted<T>>::get(self.id)261			.checked_add(1)262			.ok_or("item id overflow")?263			!= token_id264		{265			return Err("item id should be next".into());266		}267268		<Pallet<T>>::create_item(269			self,270			&caller,271			CreateItemData {272				const_data: Vec::<u8>::from(token_uri)273					.try_into()274					.map_err(|_| "token uri is too long")?,275				variable_data: BoundedVec::default(),276				owner: to,277			},278		)279		.map_err(dispatch_to_evm::<T>)?;280		Ok(true)281	}282283	/// Not implemented284	fn finish_minting(&mut self, _caller: caller) -> Result<bool> {285		Err("not implementable".into())286	}287}288289#[solidity_interface(name = "ERC721UniqueExtensions")]290impl<T: Config> NonfungibleHandle<T> {291	#[weight(<SelfWeightOf<T>>::transfer())]292	fn transfer(293		&mut self,294		caller: caller,295		to: address,296		token_id: uint256,297		_value: value,298	) -> Result<void> {299		let caller = T::CrossAccountId::from_eth(caller);300		let to = T::CrossAccountId::from_eth(to);301		let token = token_id.try_into()?;302303		<Pallet<T>>::transfer(self, &caller, &to, token).map_err(dispatch_to_evm::<T>)?;304		Ok(())305	}306307	#[weight(<SelfWeightOf<T>>::burn_from())]308	fn burn_from(309		&mut self,310		caller: caller,311		from: address,312		token_id: uint256,313		_value: value,314	) -> Result<void> {315		let caller = T::CrossAccountId::from_eth(caller);316		let from = T::CrossAccountId::from_eth(from);317		let token = token_id.try_into()?;318319		<Pallet<T>>::burn_from(self, &caller, &from, token).map_err(dispatch_to_evm::<T>)?;320		Ok(())321	}322323	fn next_token_id(&self) -> Result<uint256> {324		self.consume_store_reads(1)?;325		Ok(<TokensMinted<T>>::get(self.id)326			.checked_add(1)327			.ok_or("item id overflow")?328			.into())329	}330331	#[weight(<SelfWeightOf<T>>::set_variable_metadata(data.len() as u32))]332	fn set_variable_metadata(333		&mut self,334		caller: caller,335		token_id: uint256,336		data: bytes,337	) -> Result<void> {338		let caller = T::CrossAccountId::from_eth(caller);339		let token = token_id.try_into()?;340341		<Pallet<T>>::set_variable_metadata(342			self,343			&caller,344			token,345			data.try_into()346				.map_err(|_| "metadata size exceeded limit")?,347		)348		.map_err(dispatch_to_evm::<T>)?;349		Ok(())350	}351352	fn get_variable_metadata(&self, token_id: uint256) -> Result<bytes> {353		self.consume_store_reads(1)?;354		let token: TokenId = token_id.try_into()?;355356		Ok(<TokenData<T>>::get((self.id, token))357			.ok_or("token not found")?358			.variable_data359			.into_inner())360	}361362	#[weight(<SelfWeightOf<T>>::create_multiple_items(token_ids.len() as u32))]363	fn mint_bulk(&mut self, caller: caller, to: address, token_ids: Vec<uint256>) -> Result<bool> {364		let caller = T::CrossAccountId::from_eth(caller);365		let to = T::CrossAccountId::from_eth(to);366		let mut expected_index = <TokensMinted<T>>::get(self.id)367			.checked_add(1)368			.ok_or("item id overflow")?;369370		let total_tokens = token_ids.len();371		for id in token_ids.into_iter() {372			let id: u32 = id.try_into().map_err(|_| "token id overflow")?;373			if id != expected_index {374				return Err("item id should be next".into());375			}376			expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;377		}378		let data = (0..total_tokens)379			.map(|_| CreateItemData {380				const_data: BoundedVec::default(),381				variable_data: BoundedVec::default(),382				owner: to.clone(),383			})384			.collect();385386		<Pallet<T>>::create_multiple_items(self, &caller, data).map_err(dispatch_to_evm::<T>)?;387		Ok(true)388	}389390	#[solidity(rename_selector = "mintBulkWithTokenURI")]391	#[weight(<SelfWeightOf<T>>::create_multiple_items(tokens.len() as u32))]392	fn mint_bulk_with_token_uri(393		&mut self,394		caller: caller,395		to: address,396		tokens: Vec<(uint256, string)>,397	) -> Result<bool> {398		let caller = T::CrossAccountId::from_eth(caller);399		let to = T::CrossAccountId::from_eth(to);400		let mut expected_index = <TokensMinted<T>>::get(self.id)401			.checked_add(1)402			.ok_or("item id overflow")?;403404		let mut data = Vec::with_capacity(tokens.len());405		for (id, token_uri) in tokens {406			let id: u32 = id.try_into().map_err(|_| "token id overflow")?;407			if id != expected_index {408				panic!("item id should be next ({}) but got {}", expected_index, id);409			}410			expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;411412			data.push(CreateItemData {413				const_data: Vec::<u8>::from(token_uri)414					.try_into()415					.map_err(|_| "token uri is too long")?,416				variable_data: vec![].try_into().unwrap(),417				owner: to.clone(),418			});419		}420421		<Pallet<T>>::create_multiple_items(self, &caller, data).map_err(dispatch_to_evm::<T>)?;422		Ok(true)423	}424}425426#[solidity_interface(427	name = "UniqueNFT",428	is(429		ERC721,430		ERC721Metadata,431		ERC721Enumerable,432		ERC721UniqueExtensions,433		ERC721Mintable,434		ERC721Burnable,435	)436)]437impl<T: Config> NonfungibleHandle<T> {}438439// Not a tests, but code generators440generate_stubgen!(gen_impl, UniqueNFTCall<()>, true);441generate_stubgen!(gen_iface, UniqueNFTCall<()>, false);442443impl<T: Config> CommonEvmHandler for NonfungibleHandle<T> {444	const CODE: &'static [u8] = include_bytes!("./stubs/UniqueNFT.raw");445446	fn call(self, source: &H160, input: &[u8], value: U256) -> Option<PrecompileResult> {447		call::<T, UniqueNFTCall<T>, _>(*source, self, value, input)448	}449}