git.delta.rocks / unique-network / refs/commits / 895a035cdc96

difftreelog

source

pallets/common/src/eth.rs13.5 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	/// Whether or not this CrossAdress is valid and has meaning.129	pub status: bool,130	/// The underlying CrossAddress value. If the status is false, can be set to whatever.131	pub value: CrossAddress,132}133134/// Cross account struct135#[derive(Debug, Default, AbiCoder)]136pub struct CrossAddress {137	pub(crate) eth: address,138	pub(crate) sub: uint256,139}140141impl CrossAddress {142	/// Converts `CrossAccountId` to [`CrossAddress`] to be correctly usable with Ethereum.143	pub fn from_sub_cross_account<T>(cross_account_id: &T::CrossAccountId) -> Self144	where145		T: pallet_evm::Config,146		T::AccountId: AsRef<[u8; 32]>,147	{148		if cross_account_id.is_canonical_substrate() {149			Self::from_sub::<T>(cross_account_id.as_sub())150		} else {151			Self {152				eth: *cross_account_id.as_eth(),153				sub: Default::default(),154			}155		}156	}157	/// Creates [`CrossAddress`] from Substrate account.158	pub fn from_sub<T>(account_id: &T::AccountId) -> Self159	where160		T: pallet_evm::Config,161		T::AccountId: AsRef<[u8; 32]>,162	{163		Self {164			eth: Default::default(),165			sub: uint256::from_big_endian(account_id.as_ref()),166		}167	}168	/// Converts [`CrossAddress`] to `CrossAccountId`.169	pub fn into_sub_cross_account<T>(&self) -> evm_coder::execution::Result<T::CrossAccountId>170	where171		T: pallet_evm::Config,172		T::AccountId: From<[u8; 32]>,173	{174		if self.eth == Default::default() && self.sub == Default::default() {175			Err("All fields of cross account is zeroed".into())176		} else if self.eth == Default::default() {177			Ok(convert_uint256_to_cross_account::<T>(self.sub))178		} else if self.sub == Default::default() {179			Ok(T::CrossAccountId::from_eth(self.eth))180		} else {181			Err("All fields of cross account is non zeroed".into())182		}183	}184}185186/// Ethereum representation of collection [`PropertyKey`](up_data_structs::PropertyKey) and [`PropertyValue`](up_data_structs::PropertyValue).187#[derive(Debug, Default, AbiCoder)]188pub struct Property {189	key: evm_coder::types::string,190	value: evm_coder::types::bytes,191}192193impl TryFrom<up_data_structs::Property> for Property {194	type Error = evm_coder::execution::Error;195196	fn try_from(from: up_data_structs::Property) -> Result<Self, Self::Error> {197		let key = evm_coder::types::string::from_utf8(from.key.into())198			.map_err(|e| Self::Error::Revert(format!("utf8 conversion error: {}", e)))?;199		let value = evm_coder::types::bytes(from.value.to_vec());200		Ok(Property { key, value })201	}202}203204impl TryInto<up_data_structs::Property> for Property {205	type Error = evm_coder::execution::Error;206207	fn try_into(self) -> Result<up_data_structs::Property, Self::Error> {208		let key = <Vec<u8>>::from(self.key)209			.try_into()210			.map_err(|_| "key too large")?;211212		let value = self.value.0.try_into().map_err(|_| "value too large")?;213214		Ok(up_data_structs::Property { key, value })215	}216}217218/// [`CollectionLimits`](up_data_structs::CollectionLimits) fields representation for EVM.219#[derive(Debug, Default, Clone, Copy, AbiCoder)]220#[repr(u8)]221pub enum CollectionLimitField {222	/// How many tokens can a user have on one account.223	#[default]224	AccountTokenOwnership,225226	/// How many bytes of data are available for sponsorship.227	SponsoredDataSize,228229	/// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`]230	SponsoredDataRateLimit,231232	/// How many tokens can be mined into this collection.233	TokenLimit,234235	/// Timeouts for transfer sponsoring.236	SponsorTransferTimeout,237238	/// Timeout for sponsoring an approval in passed blocks.239	SponsorApproveTimeout,240241	/// Whether the collection owner of the collection can send tokens (which belong to other users).242	OwnerCanTransfer,243244	/// Can the collection owner burn other people's tokens.245	OwnerCanDestroy,246247	/// Is it possible to send tokens from this collection between users.248	TransferEnabled,249}250251/// [`CollectionLimits`](up_data_structs::CollectionLimits) field representation for EVM.252#[derive(Debug, Default, AbiCoder)]253pub struct CollectionLimit {254	field: CollectionLimitField,255	value: OptionUint,256}257258impl CollectionLimit {259	/// Create [`CollectionLimit`] from field and value.260	pub fn new<T>(field: CollectionLimitField, value: T) -> Self261	where262		OptionUint: From<T>,263	{264		Self {265			field,266			value: value.into(),267		}268	}269	/// Whether the field contains a value.270	pub fn has_value(&self) -> bool {271		self.value.status272	}273}274275impl TryInto<up_data_structs::CollectionLimits> for CollectionLimit {276	type Error = evm_coder::execution::Error;277278	fn try_into(self) -> Result<up_data_structs::CollectionLimits, Self::Error> {279		let value = self.value.value.try_into().map_err(|error| {280			Self::Error::Revert(format!(281				"can't convert value to u32 \"{}\" because: \"{error}\"",282				self.value.value283			))284		})?;285286		let convert_value_to_bool = || match value {287			0 => Ok(false),288			1 => Ok(true),289			_ => {290				return Err(Self::Error::Revert(format!(291					"can't convert value to boolean \"{value}\""292				)))293			}294		};295296		let mut limits = up_data_structs::CollectionLimits::default();297		match self.field {298			CollectionLimitField::AccountTokenOwnership => {299				limits.account_token_ownership_limit = Some(value);300			}301			CollectionLimitField::SponsoredDataSize => {302				limits.sponsored_data_size = Some(value);303			}304			CollectionLimitField::SponsoredDataRateLimit => {305				limits.sponsored_data_rate_limit =306					Some(up_data_structs::SponsoringRateLimit::Blocks(value));307			}308			CollectionLimitField::TokenLimit => {309				limits.token_limit = Some(value);310			}311			CollectionLimitField::SponsorTransferTimeout => {312				limits.sponsor_transfer_timeout = Some(value);313			}314			CollectionLimitField::SponsorApproveTimeout => {315				limits.sponsor_approve_timeout = Some(value);316			}317			CollectionLimitField::OwnerCanTransfer => {318				limits.owner_can_transfer = Some(convert_value_to_bool()?);319			}320			CollectionLimitField::OwnerCanDestroy => {321				limits.owner_can_destroy = Some(convert_value_to_bool()?);322			}323			CollectionLimitField::TransferEnabled => {324				limits.transfers_enabled = Some(convert_value_to_bool()?);325			}326		};327		Ok(limits)328	}329}330331/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) fields as an enumeration.332#[derive(Default, Debug, Clone, Copy, AbiCoder)]333#[repr(u8)]334pub enum CollectionPermissionField {335	/// Owner of token can nest tokens under it.336	#[default]337	TokenOwner,338339	/// Admin of token collection can nest tokens under token.340	CollectionAdmin,341}342343/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) fields as an enumeration.344#[derive(AbiCoder, Copy, Clone, Default, Debug)]345#[repr(u8)]346pub enum TokenPermissionField {347	/// Permission to change the property and property permission. See [`up_data_structs::PropertyPermission::mutable`]348	#[default]349	Mutable,350351	/// Change permission for the collection administrator. See [`up_data_structs::PropertyPermission::token_owner`]352	TokenOwner,353354	/// Permission to change the property for the owner of the token. See [`up_data_structs::PropertyPermission::collection_admin`]355	CollectionAdmin,356}357358/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) as an key and value.359#[derive(Debug, Default, AbiCoder)]360pub struct PropertyPermission {361	/// TokenPermission field.362	code: TokenPermissionField,363	/// TokenPermission value.364	value: bool,365}366367impl PropertyPermission {368	/// Make vector of [`PropertyPermission`] from [`up_data_structs::PropertyPermission`].369	pub fn into_vec(pp: up_data_structs::PropertyPermission) -> Vec<Self> {370		vec![371			PropertyPermission {372				code: TokenPermissionField::Mutable,373				value: pp.mutable,374			},375			PropertyPermission {376				code: TokenPermissionField::TokenOwner,377				value: pp.token_owner,378			},379			PropertyPermission {380				code: TokenPermissionField::CollectionAdmin,381				value: pp.collection_admin,382			},383		]384	}385386	/// Make [`up_data_structs::PropertyPermission`] from vector of [`PropertyPermission`].387	pub fn from_vec(permission: Vec<Self>) -> up_data_structs::PropertyPermission {388		let mut token_permission = up_data_structs::PropertyPermission::default();389390		for PropertyPermission { code, value } in permission {391			match code {392				TokenPermissionField::Mutable => token_permission.mutable = value,393				TokenPermissionField::TokenOwner => token_permission.token_owner = value,394				TokenPermissionField::CollectionAdmin => token_permission.collection_admin = value,395			}396		}397		token_permission398	}399}400401/// Ethereum representation of Token Property Permissions.402#[derive(Debug, Default, AbiCoder)]403pub struct TokenPropertyPermission {404	/// Token property key.405	key: evm_coder::types::string,406	/// Token property permissions.407	permissions: Vec<PropertyPermission>,408}409410impl411	From<(412		up_data_structs::PropertyKey,413		up_data_structs::PropertyPermission,414	)> for TokenPropertyPermission415{416	fn from(417		value: (418			up_data_structs::PropertyKey,419			up_data_structs::PropertyPermission,420		),421	) -> Self {422		let (key, permission) = value;423		let key = evm_coder::types::string::from_utf8(key.into_inner())424			.expect("Stored key must be valid");425		let permissions = PropertyPermission::into_vec(permission);426		Self { key, permissions }427	}428}429430impl TokenPropertyPermission {431	/// Convert vector of [`TokenPropertyPermission`] into vector of [`up_data_structs::PropertyKeyPermission`].432	pub fn into_property_key_permissions(433		permissions: Vec<TokenPropertyPermission>,434	) -> evm_coder::execution::Result<Vec<up_data_structs::PropertyKeyPermission>> {435		let mut perms = Vec::new();436437		for TokenPropertyPermission { key, permissions } in permissions {438			let token_permission = PropertyPermission::from_vec(permissions);439440			perms.push(up_data_structs::PropertyKeyPermission {441				key: key.into_bytes().try_into().map_err(|_| "too long key")?,442				permission: token_permission,443			});444		}445		Ok(perms)446	}447}448449/// Nested collections.450#[derive(Debug, Default, AbiCoder)]451pub struct CollectionNesting {452	token_owner: bool,453	ids: Vec<uint256>,454}455456impl CollectionNesting {457	/// Create [`CollectionNesting`].458	pub fn new(token_owner: bool, ids: Vec<uint256>) -> Self {459		Self { token_owner, ids }460	}461}462463/// Ethereum representation of `NestingPermissions` (see [`up_data_structs::NestingPermissions`]) field.464#[derive(Debug, Default, AbiCoder)]465pub struct CollectionNestingPermission {466	field: CollectionPermissionField,467	value: bool,468}469470impl CollectionNestingPermission {471	/// Create [`CollectionNestingPermission`].472	pub fn new(field: CollectionPermissionField, value: bool) -> Self {473		Self { field, value }474	}475}