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

difftreelog

source

pallets/common/src/eth.rs18.3 KiBsourcehistory
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! The module contains a number of functions for converting and checking ethereum identifiers.1819use alloc::format;20use sp_std::{vec, vec::Vec};21use evm_coder::{22	AbiCoder,23	types::{Address, String},24};25pub use pallet_evm::{Config, account::CrossAccountId};26use sp_core::{H160, U256};27use up_data_structs::{CollectionId, CollectionFlags};28use pallet_evm_coder_substrate::execution::Error;2930// 0x17c4e6453Cc49AAAaEACA894e6D9683e00000001 - collection 131// TODO: Unhardcode prefix32const ETH_COLLECTION_PREFIX: [u8; 16] = [33	0x17, 0xc4, 0xe6, 0x45, 0x3c, 0xc4, 0x9a, 0xaa, 0xae, 0xac, 0xa8, 0x94, 0xe6, 0xd9, 0x68, 0x3e,34];3536/// Maps the ethereum address of the collection in substrate.37pub fn map_eth_to_id(eth: &Address) -> Option<CollectionId> {38	if eth[0..16] != ETH_COLLECTION_PREFIX {39		return None;40	}41	let mut id_bytes = [0; 4];42	id_bytes.copy_from_slice(&eth[16..20]);43	Some(CollectionId(u32::from_be_bytes(id_bytes)))44}4546/// Maps the substrate collection id in ethereum.47pub fn collection_id_to_address(id: CollectionId) -> Address {48	let mut out = [0; 20];49	out[0..16].copy_from_slice(&ETH_COLLECTION_PREFIX);50	out[16..20].copy_from_slice(&u32::to_be_bytes(id.0));51	H160(out)52}5354/// Check if the ethereum address is a collection.55pub fn is_collection(address: &Address) -> bool {56	address[0..16] == ETH_COLLECTION_PREFIX57}5859/// Convert `U256` to `CrossAccountId`.60pub fn convert_uint256_to_cross_account<T: Config>(from: U256) -> T::CrossAccountId61where62	T::AccountId: From<[u8; 32]>,63{64	let mut new_admin_arr = [0_u8; 32];65	from.to_big_endian(&mut new_admin_arr);66	let account_id = T::AccountId::from(new_admin_arr);67	T::CrossAccountId::from_sub(account_id)68}6970/// Cross account struct71#[derive(Debug, Default, AbiCoder)]72pub struct CrossAddress {73	pub(crate) eth: Address,74	pub(crate) sub: U256,75}7677impl CrossAddress {78	/// Converts `CrossAccountId` to [`CrossAddress`] to be correctly usable with Ethereum.79	pub fn from_sub_cross_account<T>(cross_account_id: &T::CrossAccountId) -> Self80	where81		T: pallet_evm::Config,82		T::AccountId: AsRef<[u8; 32]>,83	{84		if cross_account_id.is_canonical_substrate() {85			Self::from_sub::<T>(cross_account_id.as_sub())86		} else {87			Self::from_eth(*cross_account_id.as_eth())88		}89	}90	/// Creates [`CrossAddress`] from Substrate account.91	pub fn from_sub<T>(account_id: &T::AccountId) -> Self92	where93		T: pallet_evm::Config,94		T::AccountId: AsRef<[u8; 32]>,95	{96		Self {97			eth: Default::default(),98			sub: U256::from_big_endian(account_id.as_ref()),99		}100	}101	/// Creates [`CrossAddress`] from Ethereum account.102	pub fn from_eth(address: Address) -> Self {103		Self {104			eth: address,105			sub: Default::default(),106		}107	}108109	/// Converts [`CrossAddress`] to `Option<CrossAccountId>`.110	pub fn into_option_sub_cross_account<T>(&self) -> Result<Option<T::CrossAccountId>, Error>111	where112		T: pallet_evm::Config,113		T::AccountId: From<[u8; 32]>,114	{115		if self.eth == Default::default() && self.sub == Default::default() {116			Ok(None)117		} else if self.eth == Default::default() {118			Ok(Some(convert_uint256_to_cross_account::<T>(self.sub)))119		} else if self.sub == Default::default() {120			Ok(Some(T::CrossAccountId::from_eth(self.eth)))121		} else {122			Err(format!("All fields of cross account is non zeroed {self:?}").into())123		}124	}125126	/// Converts [`CrossAddress`] to `CrossAccountId`.127	pub fn into_sub_cross_account<T>(&self) -> Result<T::CrossAccountId, Error>128	where129		T: pallet_evm::Config,130		T::AccountId: From<[u8; 32]>,131	{132		if self.eth == Default::default() && self.sub == Default::default() {133			Err("All fields of cross account is zeroed".into())134		} else if self.eth == Default::default() {135			Ok(convert_uint256_to_cross_account::<T>(self.sub))136		} else if self.sub == Default::default() {137			Ok(T::CrossAccountId::from_eth(self.eth))138		} else {139			Err("All fields of cross account is non zeroed".into())140		}141	}142}143144/// Type of tokens in collection145#[derive(AbiCoder, Copy, Clone, Default, Debug, PartialEq)]146#[repr(u8)]147pub enum CollectionMode {148	/// Nonfungible149	#[default]150	Nonfungible,151	/// Fungible152	Fungible,153	/// Refungible154	Refungible,155}156157/// Ethereum representation of collection [`PropertyKey`](up_data_structs::PropertyKey) and [`PropertyValue`](up_data_structs::PropertyValue).158#[derive(Debug, Default, AbiCoder)]159pub struct Property {160	key: evm_coder::types::String,161	value: evm_coder::types::Bytes,162}163164impl Property {165	/// Property key.166	pub fn key(&self) -> &str {167		self.key.as_str()168	}169170	/// Property value.171	pub fn value(&self) -> &[u8] {172		self.value.0.as_slice()173	}174}175176impl TryFrom<up_data_structs::Property> for Property {177	type Error = Error;178179	fn try_from(from: up_data_structs::Property) -> Result<Self, Self::Error> {180		let key = evm_coder::types::String::from_utf8(from.key.into())181			.map_err(|e| Self::Error::Revert(format!("utf8 conversion error: {e}")))?;182		let value = evm_coder::types::Bytes(from.value.to_vec());183		Ok(Property { key, value })184	}185}186187impl TryInto<up_data_structs::Property> for Property {188	type Error = Error;189190	fn try_into(self) -> Result<up_data_structs::Property, Self::Error> {191		let key = <Vec<u8>>::from(self.key)192			.try_into()193			.map_err(|_| "key too large")?;194195		let value = self.value.0.try_into().map_err(|_| "value too large")?;196197		Ok(up_data_structs::Property { key, value })198	}199}200201/// [`CollectionLimits`](up_data_structs::CollectionLimits) fields representation for EVM.202#[derive(Debug, Default, Clone, Copy, AbiCoder)]203#[repr(u8)]204pub enum CollectionLimitField {205	/// How many tokens can a user have on one account.206	#[default]207	AccountTokenOwnership,208209	/// How many bytes of data are available for sponsorship.210	SponsoredDataSize,211212	/// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`]213	SponsoredDataRateLimit,214215	/// How many tokens can be mined into this collection.216	TokenLimit,217218	/// Timeouts for transfer sponsoring.219	SponsorTransferTimeout,220221	/// Timeout for sponsoring an approval in passed blocks.222	SponsorApproveTimeout,223224	/// Whether the collection owner of the collection can send tokens (which belong to other users).225	OwnerCanTransfer,226227	/// Can the collection owner burn other people's tokens.228	OwnerCanDestroy,229230	/// Is it possible to send tokens from this collection between users.231	TransferEnabled,232}233234/// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM.235#[derive(Debug, Default, AbiCoder)]236pub struct CollectionLimit {237	field: CollectionLimitField,238	value: Option<U256>,239}240241impl CollectionLimit {242	/// Create [`CollectionLimit`] from field and value.243	pub fn new(field: CollectionLimitField, value: Option<u32>) -> Self {244		Self {245			field,246			value: value.map(|value| value.into()),247		}248	}249	/// Whether the field contains a value.250	pub fn has_value(&self) -> bool {251		self.value.is_some()252	}253254	/// Set corresponding property in CollectionLimits struct255	pub fn apply_limit(&self, limits: &mut up_data_structs::CollectionLimits) -> Result<(), Error> {256		let value = self257			.value258			.ok_or::<Error>("can't convert `None` value to boolean".into())?;259		let value = Some(value.try_into().map_err(|error| {260			Error::Revert(format!(261				"can't convert value to u32 \"{value}\" because: \"{error}\""262			))263		})?);264265		let convert_value_to_bool = || match value {266			Some(value) => match value {267				0 => Ok(Some(false)),268				1 => Ok(Some(true)),269				_ => Err(Error::Revert(format!(270					"can't convert value to boolean \"{value}\""271				))),272			},273			None => Ok(None),274		};275276		match self.field {277			CollectionLimitField::AccountTokenOwnership => {278				limits.account_token_ownership_limit = value;279			}280			CollectionLimitField::SponsoredDataSize => {281				limits.sponsored_data_size = value;282			}283			CollectionLimitField::SponsoredDataRateLimit => {284				limits.sponsored_data_rate_limit =285					value.map(up_data_structs::SponsoringRateLimit::Blocks);286			}287			CollectionLimitField::TokenLimit => {288				limits.token_limit = value;289			}290			CollectionLimitField::SponsorTransferTimeout => {291				limits.sponsor_transfer_timeout = value;292			}293			CollectionLimitField::SponsorApproveTimeout => {294				limits.sponsor_approve_timeout = value;295			}296			CollectionLimitField::OwnerCanTransfer => {297				limits.owner_can_transfer = convert_value_to_bool()?;298			}299			CollectionLimitField::OwnerCanDestroy => {300				limits.owner_can_destroy = convert_value_to_bool()?;301			}302			CollectionLimitField::TransferEnabled => {303				limits.transfers_enabled = convert_value_to_bool()?;304			}305		};306		Ok(())307	}308}309310/// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM.311#[derive(Debug, Default, AbiCoder)]312pub struct CollectionLimitValue {313	field: CollectionLimitField,314	value: U256,315}316317impl CollectionLimitValue {318	/// Create [`CollectionLimitValue`] from field and value.319	pub fn new(field: CollectionLimitField, value: u32) -> Self {320		Self {321			field,322			value: value.into(),323		}324	}325326	/// Set corresponding property in CollectionLimits struct327	pub fn apply_limit(&self, limits: &mut up_data_structs::CollectionLimits) -> Result<(), Error> {328		let value = self.value;329		let value: u32 = value.try_into().map_err(|error| {330			Error::Revert(format!(331				"can't convert value to u32 \"{value}\" because: \"{error}\""332			))333		})?;334335		let convert_value_to_bool = || match value {336			0 => Ok(Some(false)),337			1 => Ok(Some(true)),338			_ => Err(Error::Revert(format!(339				"can't convert value to boolean \"{value}\""340			))),341		};342343		match self.field {344			CollectionLimitField::AccountTokenOwnership => {345				limits.account_token_ownership_limit = Some(value);346			}347			CollectionLimitField::SponsoredDataSize => {348				limits.sponsored_data_size = Some(value);349			}350			CollectionLimitField::SponsoredDataRateLimit => {351				limits.sponsored_data_rate_limit =352					Some(up_data_structs::SponsoringRateLimit::Blocks(value));353			}354			CollectionLimitField::TokenLimit => {355				limits.token_limit = Some(value);356			}357			CollectionLimitField::SponsorTransferTimeout => {358				limits.sponsor_transfer_timeout = Some(value);359			}360			CollectionLimitField::SponsorApproveTimeout => {361				limits.sponsor_approve_timeout = Some(value);362			}363			CollectionLimitField::OwnerCanTransfer => {364				limits.owner_can_transfer = convert_value_to_bool()?;365			}366			CollectionLimitField::OwnerCanDestroy => {367				limits.owner_can_destroy = convert_value_to_bool()?;368			}369			CollectionLimitField::TransferEnabled => {370				limits.transfers_enabled = convert_value_to_bool()?;371			}372		};373		Ok(())374	}375}376377impl TryInto<up_data_structs::CollectionLimits> for CollectionLimit {378	type Error = Error;379380	fn try_into(self) -> Result<up_data_structs::CollectionLimits, Self::Error> {381		let mut limits = up_data_structs::CollectionLimits::default();382		self.apply_limit(&mut limits)?;383		Ok(limits)384	}385}386387impl FromIterator<CollectionLimitValue> for Result<up_data_structs::CollectionLimits, Error> {388	fn from_iter<T: IntoIterator<Item = CollectionLimitValue>>(389		iter: T,390	) -> Result<up_data_structs::CollectionLimits, Error> {391		let mut limits = up_data_structs::CollectionLimits::default();392		for value in iter.into_iter() {393			value.apply_limit(&mut limits)?;394		}395		Ok(limits)396	}397}398399/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) fields as an enumeration.400#[derive(Default, Debug, Clone, Copy, AbiCoder)]401#[repr(u8)]402pub enum CollectionPermissionField {403	/// Owner of token can nest tokens under it.404	#[default]405	TokenOwner,406407	/// Admin of token collection can nest tokens under token.408	CollectionAdmin,409}410411/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) fields as an enumeration.412#[derive(AbiCoder, Copy, Clone, Default, Debug)]413#[repr(u8)]414pub enum TokenPermissionField {415	/// Permission to change the property and property permission. See [`up_data_structs::PropertyPermission::mutable`]416	#[default]417	Mutable,418419	/// Change permission for the collection administrator. See [`up_data_structs::PropertyPermission::token_owner`]420	TokenOwner,421422	/// Permission to change the property for the owner of the token. See [`up_data_structs::PropertyPermission::collection_admin`]423	CollectionAdmin,424}425426/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) as an key and value.427#[derive(Debug, Default, AbiCoder)]428pub struct PropertyPermission {429	/// TokenPermission field.430	code: TokenPermissionField,431	/// TokenPermission value.432	value: bool,433}434435impl PropertyPermission {436	/// Make vector of [`PropertyPermission`] from [`up_data_structs::PropertyPermission`].437	pub fn into_vec(pp: up_data_structs::PropertyPermission) -> Vec<Self> {438		vec![439			PropertyPermission {440				code: TokenPermissionField::Mutable,441				value: pp.mutable,442			},443			PropertyPermission {444				code: TokenPermissionField::TokenOwner,445				value: pp.token_owner,446			},447			PropertyPermission {448				code: TokenPermissionField::CollectionAdmin,449				value: pp.collection_admin,450			},451		]452	}453454	/// Make [`up_data_structs::PropertyPermission`] from vector of [`PropertyPermission`].455	pub fn from_vec(permission: Vec<Self>) -> up_data_structs::PropertyPermission {456		let mut token_permission = up_data_structs::PropertyPermission::default();457458		for PropertyPermission { code, value } in permission {459			match code {460				TokenPermissionField::Mutable => token_permission.mutable = value,461				TokenPermissionField::TokenOwner => token_permission.token_owner = value,462				TokenPermissionField::CollectionAdmin => token_permission.collection_admin = value,463			}464		}465		token_permission466	}467}468469/// Ethereum representation of Token Property Permissions.470#[derive(Debug, Default, AbiCoder)]471pub struct TokenPropertyPermission {472	/// Token property key.473	key: evm_coder::types::String,474	/// Token property permissions.475	permissions: Vec<PropertyPermission>,476}477478impl479	From<(480		up_data_structs::PropertyKey,481		up_data_structs::PropertyPermission,482	)> for TokenPropertyPermission483{484	fn from(485		value: (486			up_data_structs::PropertyKey,487			up_data_structs::PropertyPermission,488		),489	) -> Self {490		let (key, permission) = value;491		let key = evm_coder::types::String::from_utf8(key.into_inner())492			.expect("Stored key must be valid");493		let permissions = PropertyPermission::into_vec(permission);494		Self { key, permissions }495	}496}497498impl TokenPropertyPermission {499	/// Convert vector of [`TokenPropertyPermission`] into vector of [`up_data_structs::PropertyKeyPermission`].500	pub fn into_property_key_permissions(501		permissions: Vec<TokenPropertyPermission>,502	) -> Result<Vec<up_data_structs::PropertyKeyPermission>, Error> {503		let mut perms = Vec::new();504505		for TokenPropertyPermission { key, permissions } in permissions {506			let token_permission = PropertyPermission::from_vec(permissions);507508			perms.push(up_data_structs::PropertyKeyPermission {509				key: key.into_bytes().try_into().map_err(|_| "too long key")?,510				permission: token_permission,511			});512		}513		Ok(perms)514	}515}516517/// Data for creation token with uri.518#[derive(Debug, AbiCoder)]519pub struct TokenUri {520	/// Id of new token.521	pub id: U256,522523	/// Uri of new token.524	pub uri: String,525}526527/// Nested collections and permissions528#[derive(Debug, Default, AbiCoder)]529pub struct CollectionNestingAndPermission {530	/// Owner of token can nest tokens under it.531	pub token_owner: bool,532	/// Admin of token collection can nest tokens under token.533	pub collection_admin: bool,534	/// If set - only tokens from specified collections can be nested.535	pub restricted: Vec<Address>,536}537538impl CollectionNestingAndPermission {539	/// Create [`CollectionNesting`].540	pub fn new(token_owner: bool, collection_admin: bool, restricted: Vec<Address>) -> Self {541		Self {542			token_owner,543			collection_admin,544			restricted,545		}546	}547}548549/// Collection properties550#[derive(Debug, Default, AbiCoder)]551pub struct CreateCollectionData {552	/// Collection name553	pub name: String,554	/// Collection description555	pub description: String,556	/// Token prefix557	pub token_prefix: String,558	/// Token type (NFT, FT or RFT)559	pub mode: CollectionMode,560	/// Fungible token precision561	pub decimals: u8,562	/// Custom Properties563	pub properties: Vec<Property>,564	/// Permissions for token properties565	pub token_property_permissions: Vec<TokenPropertyPermission>,566	/// Collection admins567	pub admin_list: Vec<CrossAddress>,568	/// Nesting settings569	pub nesting_settings: CollectionNestingAndPermission,570	/// Collection limits571	pub limits: Vec<CollectionLimitValue>,572	/// Collection sponsor573	pub pending_sponsor: CrossAddress,574	/// Extra collection flags575	pub flags: CollectionFlags,576}577578/// Nested collections.579#[derive(Debug, Default, AbiCoder)]580pub struct CollectionNesting {581	token_owner: bool,582	ids: Vec<U256>,583}584585impl CollectionNesting {586	/// Create [`CollectionNesting`].587	pub fn new(token_owner: bool, ids: Vec<U256>) -> Self {588		Self { token_owner, ids }589	}590}591592/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) field.593#[derive(Debug, Default, AbiCoder)]594pub struct CollectionNestingPermission {595	field: CollectionPermissionField,596	value: bool,597}598599impl CollectionNestingPermission {600	/// Create [`CollectionNestingPermission`].601	pub fn new(field: CollectionPermissionField, value: bool) -> Self {602		Self { field, value }603	}604}605606/// Ethereum representation of `AccessMode` (see [`up_data_structs::AccessMode`]).607#[derive(AbiCoder, Copy, Clone, Default, Debug)]608#[repr(u8)]609pub enum AccessMode {610	/// Access grant for owner and admins. Used as default.611	#[default]612	Normal,613	/// Like a [`Normal`](AccessMode::Normal) but also users in allow list.614	AllowList,615}616617impl From<up_data_structs::AccessMode> for AccessMode {618	fn from(value: up_data_structs::AccessMode) -> Self {619		match value {620			up_data_structs::AccessMode::Normal => AccessMode::Normal,621			up_data_structs::AccessMode::AllowList => AccessMode::AllowList,622		}623	}624}625626impl From<AccessMode> for up_data_structs::AccessMode {627	fn from(value: AccessMode) -> Self {628		match value {629			AccessMode::Normal => up_data_structs::AccessMode::Normal,630			AccessMode::AllowList => up_data_structs::AccessMode::AllowList,631		}632	}633}