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

difftreelog

source

pallets/common/src/eth.rs13.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::{uint256, address},24};25pub use pallet_evm::{Config, account::CrossAccountId};26use sp_core::H160;27use up_data_structs::CollectionId;2829// 0x17c4e6453Cc49AAAaEACA894e6D9683e00000001 - collection 130// TODO: Unhardcode prefix31const ETH_COLLECTION_PREFIX: [u8; 16] = [32	0x17, 0xc4, 0xe6, 0x45, 0x3c, 0xc4, 0x9a, 0xaa, 0xae, 0xac, 0xa8, 0x94, 0xe6, 0xd9, 0x68, 0x3e,33];3435/// Maps the ethereum address of the collection in substrate.36pub fn map_eth_to_id(eth: &H160) -> Option<CollectionId> {37	if eth[0..16] != ETH_COLLECTION_PREFIX {38		return None;39	}40	let mut id_bytes = [0; 4];41	id_bytes.copy_from_slice(&eth[16..20]);42	Some(CollectionId(u32::from_be_bytes(id_bytes)))43}4445/// Maps the substrate collection id in ethereum.46pub fn collection_id_to_address(id: CollectionId) -> H160 {47	let mut out = [0; 20];48	out[0..16].copy_from_slice(&ETH_COLLECTION_PREFIX);49	out[16..20].copy_from_slice(&u32::to_be_bytes(id.0));50	H160(out)51}5253/// Check if the ethereum address is a collection.54pub fn is_collection(address: &H160) -> bool {55	address[0..16] == ETH_COLLECTION_PREFIX56}5758/// Convert `uint256` to `CrossAccountId`.59pub fn convert_uint256_to_cross_account<T: Config>(from: uint256) -> T::CrossAccountId60where61	T::AccountId: From<[u8; 32]>,62{63	let mut new_admin_arr = [0_u8; 32];64	from.to_big_endian(&mut new_admin_arr);65	let account_id = T::AccountId::from(new_admin_arr);66	T::CrossAccountId::from_sub(account_id)67}6869/// Ethereum representation of Optional value with uint256.70#[derive(Debug, Default, AbiCoder)]71pub struct OptionUint {72	status: bool,73	value: uint256,74}7576impl From<u32> for OptionUint {77	fn from(value: u32) -> Self {78		Self {79			status: true,80			value: uint256::from(value),81		}82	}83}8485impl From<Option<u32>> for OptionUint {86	fn from(value: Option<u32>) -> Self {87		match value {88			Some(value) => Self {89				status: true,90				value: value.into(),91			},92			None => Self {93				status: false,94				value: Default::default(),95			},96		}97	}98}99100impl From<bool> for OptionUint {101	fn from(value: bool) -> Self {102		Self {103			status: true,104			value: if value {105				uint256::from(1)106			} else {107				Default::default()108			},109		}110	}111}112113impl From<Option<bool>> for OptionUint {114	fn from(value: Option<bool>) -> Self {115		match value {116			Some(value) => Self::from(value),117			None => Self {118				status: false,119				value: Default::default(),120			},121		}122	}123}124125/// Ethereum representation of Optional value with CrossAddress.126#[derive(Debug, Default, AbiCoder)]127pub struct OptionCrossAddress {128	pub status: bool,129	pub value: CrossAddress,130}131132/// Cross account struct133#[derive(Debug, Default, AbiCoder)]134pub struct CrossAddress {135	pub(crate) eth: address,136	pub(crate) sub: uint256,137}138139impl CrossAddress {140	/// Converts `CrossAccountId` to [`CrossAddress`]141	pub fn from_sub_cross_account<T>(cross_account_id: &T::CrossAccountId) -> Self142	where143		T: pallet_evm::Config,144		T::AccountId: AsRef<[u8; 32]>,145	{146		if cross_account_id.is_canonical_substrate() {147			Self::from_sub::<T>(cross_account_id.as_sub())148		} else {149			Self {150				eth: *cross_account_id.as_eth(),151				sub: Default::default(),152			}153		}154	}155	/// Creates [`CrossAddress`] from substrate account156	pub fn from_sub<T>(account_id: &T::AccountId) -> Self157	where158		T: pallet_evm::Config,159		T::AccountId: AsRef<[u8; 32]>,160	{161		Self {162			eth: Default::default(),163			sub: uint256::from_big_endian(account_id.as_ref()),164		}165	}166	/// Converts [`CrossAddress`] to `CrossAccountId`167	pub fn into_sub_cross_account<T>(&self) -> evm_coder::execution::Result<T::CrossAccountId>168	where169		T: pallet_evm::Config,170		T::AccountId: From<[u8; 32]>,171	{172		if self.eth == Default::default() && self.sub == Default::default() {173			Err("All fields of cross account is zeroed".into())174		} else if self.eth == Default::default() {175			Ok(convert_uint256_to_cross_account::<T>(self.sub))176		} else if self.sub == Default::default() {177			Ok(T::CrossAccountId::from_eth(self.eth))178		} else {179			Err("All fields of cross account is non zeroed".into())180		}181	}182}183184/// Ethereum representation of collection [`PropertyKey`](up_data_structs::PropertyKey) and [`PropertyValue`](up_data_structs::PropertyValue).185#[derive(Debug, Default, AbiCoder)]186pub struct Property {187	key: evm_coder::types::string,188	value: evm_coder::types::bytes,189}190191impl TryFrom<up_data_structs::Property> for Property {192	type Error = evm_coder::execution::Error;193194	fn try_from(from: up_data_structs::Property) -> Result<Self, Self::Error> {195		let key = evm_coder::types::string::from_utf8(from.key.into())196			.map_err(|e| Self::Error::Revert(format!("utf8 conversion error: {}", e)))?;197		let value = evm_coder::types::bytes(from.value.to_vec());198		Ok(Property { key, value })199	}200}201202impl TryInto<up_data_structs::Property> for Property {203	type Error = evm_coder::execution::Error;204205	fn try_into(self) -> Result<up_data_structs::Property, Self::Error> {206		let key = <Vec<u8>>::from(self.key)207			.try_into()208			.map_err(|_| "key too large")?;209210		let value = self.value.0.try_into().map_err(|_| "value too large")?;211212		Ok(up_data_structs::Property { key, value })213	}214}215216/// [`CollectionLimits`](up_data_structs::CollectionLimits) fields representation for EVM.217#[derive(Debug, Default, Clone, Copy, AbiCoder)]218#[repr(u8)]219pub enum CollectionLimitField {220	/// How many tokens can a user have on one account.221	#[default]222	AccountTokenOwnership,223224	/// How many bytes of data are available for sponsorship.225	SponsoredDataSize,226227	/// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`]228	SponsoredDataRateLimit,229230	/// How many tokens can be mined into this collection.231	TokenLimit,232233	/// Timeouts for transfer sponsoring.234	SponsorTransferTimeout,235236	/// Timeout for sponsoring an approval in passed blocks.237	SponsorApproveTimeout,238239	/// Whether the collection owner of the collection can send tokens (which belong to other users).240	OwnerCanTransfer,241242	/// Can the collection owner burn other people's tokens.243	OwnerCanDestroy,244245	/// Is it possible to send tokens from this collection between users.246	TransferEnabled,247}248249/// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM.250#[derive(Debug, Default, AbiCoder)]251pub struct CollectionLimit {252	field: CollectionLimitField,253	value: OptionUint,254}255256impl CollectionLimit {257	/// Create [`CollectionLimit`] from field and value.258	pub fn new<T>(field: CollectionLimitField, value: T) -> Self259	where260		OptionUint: From<T>,261	{262		Self {263			field,264			value: value.into(),265		}266	}267	/// Whether the field contains a value.268	pub fn has_value(&self) -> bool {269		self.value.status270	}271}272273impl TryInto<up_data_structs::CollectionLimits> for CollectionLimit {274	type Error = evm_coder::execution::Error;275276	fn try_into(self) -> Result<up_data_structs::CollectionLimits, Self::Error> {277		let value = self.value.value.try_into().map_err(|error| {278			Self::Error::Revert(format!(279				"can't convert value to u32 \"{}\" because: \"{error}\"",280				self.value.value281			))282		})?;283284		let convert_value_to_bool = || match value {285			0 => Ok(false),286			1 => Ok(true),287			_ => {288				return Err(Self::Error::Revert(format!(289					"can't convert value to boolean \"{value}\""290				)))291			}292		};293294		let mut limits = up_data_structs::CollectionLimits::default();295		match self.field {296			CollectionLimitField::AccountTokenOwnership => {297				limits.account_token_ownership_limit = Some(value);298			}299			CollectionLimitField::SponsoredDataSize => {300				limits.sponsored_data_size = Some(value);301			}302			CollectionLimitField::SponsoredDataRateLimit => {303				limits.sponsored_data_rate_limit =304					Some(up_data_structs::SponsoringRateLimit::Blocks(value));305			}306			CollectionLimitField::TokenLimit => {307				limits.token_limit = Some(value);308			}309			CollectionLimitField::SponsorTransferTimeout => {310				limits.sponsor_transfer_timeout = Some(value);311			}312			CollectionLimitField::SponsorApproveTimeout => {313				limits.sponsor_approve_timeout = Some(value);314			}315			CollectionLimitField::OwnerCanTransfer => {316				limits.owner_can_transfer = Some(convert_value_to_bool()?);317			}318			CollectionLimitField::OwnerCanDestroy => {319				limits.owner_can_destroy = Some(convert_value_to_bool()?);320			}321			CollectionLimitField::TransferEnabled => {322				limits.transfers_enabled = Some(convert_value_to_bool()?);323			}324		};325		Ok(limits)326	}327}328329/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) fields as an enumeration.330#[derive(Default, Debug, Clone, Copy, AbiCoder)]331#[repr(u8)]332pub enum CollectionPermissionField {333	/// Owner of token can nest tokens under it.334	#[default]335	TokenOwner,336337	/// Admin of token collection can nest tokens under token.338	CollectionAdmin,339}340341/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) fields as an enumeration.342#[derive(AbiCoder, Copy, Clone, Default, Debug)]343#[repr(u8)]344pub enum TokenPermissionField {345	/// Permission to change the property and property permission. See [`up_data_structs::PropertyPermission::mutable`]346	#[default]347	Mutable,348349	/// Change permission for the collection administrator. See [`up_data_structs::PropertyPermission::token_owner`]350	TokenOwner,351352	/// Permission to change the property for the owner of the token. See [`up_data_structs::PropertyPermission::collection_admin`]353	CollectionAdmin,354}355356/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) as an key and value.357#[derive(Debug, Default, AbiCoder)]358pub struct PropertyPermission {359	/// TokenPermission field.360	code: TokenPermissionField,361	/// TokenPermission value.362	value: bool,363}364365impl PropertyPermission {366	/// Make vector of [`PropertyPermission`] from [`up_data_structs::PropertyPermission`].367	pub fn into_vec(pp: up_data_structs::PropertyPermission) -> Vec<Self> {368		vec![369			PropertyPermission {370				code: TokenPermissionField::Mutable,371				value: pp.mutable,372			},373			PropertyPermission {374				code: TokenPermissionField::TokenOwner,375				value: pp.token_owner,376			},377			PropertyPermission {378				code: TokenPermissionField::CollectionAdmin,379				value: pp.collection_admin,380			},381		]382	}383384	/// Make [`up_data_structs::PropertyPermission`] from vector of [`PropertyPermission`].385	pub fn from_vec(permission: Vec<Self>) -> up_data_structs::PropertyPermission {386		let mut token_permission = up_data_structs::PropertyPermission::default();387388		for PropertyPermission { code, value } in permission {389			match code {390				TokenPermissionField::Mutable => token_permission.mutable = value,391				TokenPermissionField::TokenOwner => token_permission.token_owner = value,392				TokenPermissionField::CollectionAdmin => token_permission.collection_admin = value,393			}394		}395		token_permission396	}397}398399/// Ethereum representation of Token Property Permissions.400#[derive(Debug, Default, AbiCoder)]401pub struct TokenPropertyPermission {402	/// Token property key.403	key: evm_coder::types::string,404	/// Token property permissions.405	permissions: Vec<PropertyPermission>,406}407408impl409	From<(410		up_data_structs::PropertyKey,411		up_data_structs::PropertyPermission,412	)> for TokenPropertyPermission413{414	fn from(415		value: (416			up_data_structs::PropertyKey,417			up_data_structs::PropertyPermission,418		),419	) -> Self {420		let (key, permission) = value;421		let key = evm_coder::types::string::from_utf8(key.into_inner())422			.expect("Stored key must be valid");423		let permissions = PropertyPermission::into_vec(permission);424		Self { key, permissions }425	}426}427428impl TokenPropertyPermission {429	/// Convert vector of [`TokenPropertyPermission`] into vector of [`up_data_structs::PropertyKeyPermission`].430	pub fn into_property_key_permissions(431		permissions: Vec<TokenPropertyPermission>,432	) -> evm_coder::execution::Result<Vec<up_data_structs::PropertyKeyPermission>> {433		let mut perms = Vec::new();434435		for TokenPropertyPermission { key, permissions } in permissions {436			let token_permission = PropertyPermission::from_vec(permissions);437438			perms.push(up_data_structs::PropertyKeyPermission {439				key: key.into_bytes().try_into().map_err(|_| "too long key")?,440				permission: token_permission,441			});442		}443		Ok(perms)444	}445}446447/// Nested collections.448#[derive(Debug, Default, AbiCoder)]449pub struct CollectionNesting {450	token_owner: bool,451	ids: Vec<uint256>,452}453454impl CollectionNesting {455	/// Create [`CollectionNesting`].456	pub fn new(token_owner: bool, ids: Vec<uint256>) -> Self {457		Self { token_owner, ids }458	}459}460461/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) field.462#[derive(Debug, Default, AbiCoder)]463pub struct CollectionNestingPermission {464	field: CollectionPermissionField,465	value: bool,466}467468impl CollectionNestingPermission {469	/// Create [`CollectionNestingPermission`].470	pub fn new(field: CollectionPermissionField, value: bool) -> Self {471		Self { field, value }472	}473}