git.delta.rocks / unique-network / refs/commits / 905f9b3923e7

difftreelog

refactor fix complex value type support in evm-coder

Yaroslav Bolyukin2023-08-29parent: #1a4aa2d.patch.diff
in: master

14 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2653,8 +2653,9 @@
 
 [[package]]
 name = "evm-coder"
-version = "0.3.6"
-source = "git+https://github.com/uniquenetwork/evm-coder?tag=v0.3.6#be59ae41a5d2ec9389bae22d54f81122b6ba55a4"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b88ae5a449e7e9dfef59c0dd2df396bc56d81a6f4e297490c0c64aa73f7f7ad4"
 dependencies = [
  "ethereum",
  "evm-coder-procedural",
@@ -2665,8 +2666,9 @@
 
 [[package]]
 name = "evm-coder-procedural"
-version = "0.3.6"
-source = "git+https://github.com/uniquenetwork/evm-coder?tag=v0.3.6#be59ae41a5d2ec9389bae22d54f81122b6ba55a4"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6223c1063c1f53380b4b9aaf2e4d82eba4808c661c61265619b804b09b7a6b1a"
 dependencies = [
  "Inflector",
  "hex",
modifiedCargo.tomldiffbeforeafterboth
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -27,7 +27,7 @@
 [workspace.dependencies]
 # Unique
 app-promotion-rpc = { path = "primitives/app_promotion_rpc", default-features = false }
-evm-coder = { git = "https://github.com/uniquenetwork/evm-coder", tag = "v0.3.6", default-features = false, features = [
+evm-coder = { version = "0.4.2", default-features = false, features = [
 	'bondrewd',
 ] }
 pallet-app-promotion = { path = "pallets/app-promotion", default-features = false }
modifiedpallets/common/Cargo.tomldiffbeforeafterboth
--- a/pallets/common/Cargo.toml
+++ b/pallets/common/Cargo.toml
@@ -35,7 +35,8 @@
 	"sp-std/std",
 	"up-data-structs/std",
 	"up-pov-estimate-rpc/std",
+	"evm-coder/std",
 ]
-stubgen = ["evm-coder/stubgen"]
+stubgen = ["evm-coder/stubgen", "up-data-structs/stubgen"]
 tests = []
 try-runtime = ["frame-support/try-runtime"]
modifiedpallets/common/src/eth.rsdiffbeforeafterboth
before · pallets/common/src/eth.rs
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 sponsor553	pub pending_sponsor: CrossAddress,554	/// Collection name555	pub name: String,556	/// Collection description557	pub description: String,558	/// Token prefix559	pub token_prefix: String,560	/// Token type (NFT, FT or RFT)561	pub mode: CollectionMode,562	/// Fungible token precision563	pub decimals: u8,564	/// Custom Properties565	pub properties: Vec<Property>,566	/// Permissions for token properties567	pub token_property_permissions: Vec<TokenPropertyPermission>,568	/// Collection admins569	pub admin_list: Vec<CrossAddress>,570	/// Nesting settings571	pub nesting_settings: CollectionNestingAndPermission,572	/// Collection limits573	pub limits: Vec<CollectionLimitValue>,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}
after · pallets/common/src/eth.rs
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}
modifiedpallets/evm-coder-substrate/src/lib.rsdiffbeforeafterboth
--- a/pallets/evm-coder-substrate/src/lib.rs
+++ b/pallets/evm-coder-substrate/src/lib.rs
@@ -26,7 +26,7 @@
 use frame_support::dispatch::Weight;
 
 use core::marker::PhantomData;
-use sp_std::cell::RefCell;
+use sp_std::{cell::RefCell, vec::Vec};
 
 use codec::Decode;
 use frame_support::pallet_prelude::DispatchError;
@@ -46,8 +46,8 @@
 pub use spez::spez;
 
 use evm_coder::{
-	abi::{AbiReader, AbiWrite, AbiWriter},
 	types::{Msg, Value},
+	AbiEncode,
 };
 
 pub use pallet::*;
@@ -168,7 +168,7 @@
 	pub fn evm_to_precompile_output(
 		self,
 		handle: &mut impl PrecompileHandle,
-		result: execution::Result<Option<AbiWriter>>,
+		result: execution::Result<Option<Vec<u8>>>,
 	) -> Option<PrecompileResult> {
 		use execution::Error;
 		// We ignore error here, as it should not occur, as we have our own bookkeeping of gas
@@ -176,18 +176,13 @@
 		Some(match result {
 			Ok(Some(v)) => Ok(PrecompileOutput {
 				exit_status: ExitSucceed::Returned,
-				output: v.finish(),
+				output: v,
 			}),
 			Ok(None) => return None,
-			Err(Error::Revert(e)) => {
-				let mut writer = AbiWriter::new_call(evm_coder::fn_selector!(Error(string)));
-				(&e as &str).abi_write(&mut writer);
-
-				Err(PrecompileFailure::Revert {
-					exit_status: ExitRevert::Reverted,
-					output: writer.finish(),
-				})
-			}
+			Err(Error::Revert(e)) => Err(PrecompileFailure::Revert {
+				exit_status: ExitRevert::Reverted,
+				output: (&e as &str,).abi_encode_call(evm_coder::fn_selector!(Error(string))),
+			}),
 			Err(Error::Fatal(f)) => Err(PrecompileFailure::Fatal { exit_status: f }),
 			Err(Error::Error(e)) => Err(e.into()),
 		})
@@ -266,7 +261,7 @@
 	C: evm_coder::Call + PreDispatch,
 	E: evm_coder::Callable<C> + WithRecorder<T>,
 	H: PrecompileHandle,
-	execution::ResultWithPostInfo<AbiWriter>: From<ResultWithPostInfoOf<E, AbiWriter>>,
+	execution::ResultWithPostInfo<Vec<u8>>: From<ResultWithPostInfoOf<E, Vec<u8>>>,
 {
 	let result = call_internal(
 		handle.context().caller,
@@ -282,18 +277,16 @@
 	e: &mut E,
 	value: Value,
 	input: &[u8],
-) -> execution::Result<Option<AbiWriter>>
+) -> execution::Result<Option<Vec<u8>>>
 where
 	T: Config,
 	C: evm_coder::Call + PreDispatch,
 	E: Contract + evm_coder::Callable<C> + WithRecorder<T>,
-	execution::ResultWithPostInfo<AbiWriter>: From<ResultWithPostInfoOf<E, AbiWriter>>,
+	execution::ResultWithPostInfo<Vec<u8>>: From<ResultWithPostInfoOf<E, Vec<u8>>>,
 {
-	let (selector, mut reader) = AbiReader::new_call(input)?;
-	let call = C::parse(selector, &mut reader)?;
+	let call = C::parse_full(input)?;
 	if call.is_none() {
-		let selector = u32::from_be_bytes(selector);
-		return Err(format!("unrecognized selector: 0x{selector:0<8x}").into());
+		return Err("unrecognized selector".into());
 	}
 	let call = call.unwrap();
 
modifiedpallets/evm-contract-helpers/src/eth.rsdiffbeforeafterboth
--- a/pallets/evm-contract-helpers/src/eth.rs
+++ b/pallets/evm-contract-helpers/src/eth.rs
@@ -19,7 +19,7 @@
 extern crate alloc;
 use core::marker::PhantomData;
 use evm_coder::{
-	abi::{AbiWriter, AbiType},
+	abi::{AbiType, AbiEncode},
 	generate_stubgen, solidity_interface,
 	types::*,
 	ToLog,
@@ -370,11 +370,8 @@
 		{
 			return Some(Err(PrecompileFailure::Revert {
 				exit_status: ExitRevert::Reverted,
-				output: {
-					let mut writer = AbiWriter::new_call(evm_coder::fn_selector!(Error(string)));
-					writer.string("Target contract is allowlisted");
-					writer.finish()
-				},
+				output: ("target contract is allowlisted",)
+					.abi_encode_call(evm_coder::fn_selector!(Error(string))),
 			}));
 		}
 
modifiedpallets/gov-origins/src/lib.rsdiffbeforeafterboth
--- a/pallets/gov-origins/src/lib.rs
+++ b/pallets/gov-origins/src/lib.rs
@@ -31,6 +31,7 @@
 
 	#[derive(PartialEq, Eq, Clone, MaxEncodedLen, Encode, Decode, TypeInfo, RuntimeDebug)]
 	#[pallet::origin]
+	#[non_exhaustive]
 	pub enum Origin {
 		/// Origin able to send proposal from fellowship collective to democracy pallet.
 		FellowshipProposition,
modifiedpallets/unique/src/eth/mod.rsdiffbeforeafterboth
--- a/pallets/unique/src/eth/mod.rs
+++ b/pallets/unique/src/eth/mod.rs
@@ -139,116 +139,114 @@
 	T: Config + pallet_common::Config + pallet_nonfungible::Config + pallet_refungible::Config,
 	T::AccountId: From<[u8; 32]>,
 {
-	/*
-		/// Create a collection
-		/// @return address Address of the newly created collection
-		#[weight(<SelfWeightOf<T>>::create_collection())]
-		#[solidity(rename_selector = "createCollection")]
-		fn create_collection(
-			&mut self,
-			caller: Caller,
-			value: Value,
-			data: eth::CreateCollectionData,
-		) -> Result<Address> {
-			let (caller, name, description, token_prefix) =
-				convert_data::<T>(caller, data.name, data.description, data.token_prefix)?;
-			if data.mode != eth::CollectionMode::Fungible && data.decimals != 0 {
-				return Err("decimals are only supported for NFT and RFT collections".into());
-			}
-			let mode = match data.mode {
-				eth::CollectionMode::Fungible => CollectionMode::Fungible(data.decimals),
-				eth::CollectionMode::Nonfungible => CollectionMode::NFT,
-				eth::CollectionMode::Refungible => CollectionMode::ReFungible,
-			};
+	/// Create a collection
+	/// @return address Address of the newly created collection
+	#[weight(<SelfWeightOf<T>>::create_collection())]
+	#[solidity(rename_selector = "createCollection")]
+	fn create_collection(
+		&mut self,
+		caller: Caller,
+		value: Value,
+		data: eth::CreateCollectionData,
+	) -> Result<Address> {
+		let (caller, name, description, token_prefix) =
+			convert_data::<T>(caller, data.name, data.description, data.token_prefix)?;
+		if data.mode != eth::CollectionMode::Fungible && data.decimals != 0 {
+			return Err("decimals are only supported for NFT and RFT collections".into());
+		}
+		let mode = match data.mode {
+			eth::CollectionMode::Fungible => CollectionMode::Fungible(data.decimals),
+			eth::CollectionMode::Nonfungible => CollectionMode::NFT,
+			eth::CollectionMode::Refungible => CollectionMode::ReFungible,
+		};
 
-			let properties: BoundedVec<_, _> = data
-				.properties
-				.into_iter()
-				.map(eth::Property::try_into)
-				.collect::<Result<Vec<_>>>()?
-				.try_into()
-				.map_err(|_| "too many properties")?;
+		let properties: BoundedVec<_, _> = data
+			.properties
+			.into_iter()
+			.map(eth::Property::try_into)
+			.collect::<Result<Vec<_>>>()?
+			.try_into()
+			.map_err(|_| "too many properties")?;
 
-			let token_property_permissions =
-				eth::TokenPropertyPermission::into_property_key_permissions(
-					data.token_property_permissions,
-				)?
-				.try_into()
-				.map_err(|_| "too many property permissions")?;
+		let token_property_permissions =
+			eth::TokenPropertyPermission::into_property_key_permissions(
+				data.token_property_permissions,
+			)?
+			.try_into()
+			.map_err(|_| "too many property permissions")?;
 
-			let limits = if !data.limits.is_empty() {
-				Some(
-					data.limits
-						.into_iter()
-						.collect::<Result<up_data_structs::CollectionLimits>>()?,
-				)
-			} else {
-				None
-			};
+		let limits = if !data.limits.is_empty() {
+			Some(
+				data.limits
+					.into_iter()
+					.collect::<Result<up_data_structs::CollectionLimits>>()?,
+			)
+		} else {
+			None
+		};
 
-			let pending_sponsor = data.pending_sponsor.into_option_sub_cross_account::<T>()?;
+		let pending_sponsor = data.pending_sponsor.into_option_sub_cross_account::<T>()?;
 
-			let restricted = if !data.nesting_settings.restricted.is_empty() {
-				Some(
-					data.nesting_settings
-						.restricted
-						.iter()
-						.map(map_eth_to_id)
-						.collect::<Option<BTreeSet<_>>>()
-						.ok_or("can't convert address into collection id")?
-						.try_into()
-						.map_err(|_| "too many collections")?,
-				)
-			} else {
-				None
-			};
+		let restricted = if !data.nesting_settings.restricted.is_empty() {
+			Some(
+				data.nesting_settings
+					.restricted
+					.iter()
+					.map(map_eth_to_id)
+					.collect::<Option<BTreeSet<_>>>()
+					.ok_or("can't convert address into collection id")?
+					.try_into()
+					.map_err(|_| "too many collections")?,
+			)
+		} else {
+			None
+		};
 
-			let admin_list = data
-				.admin_list
-				.into_iter()
-				.map(|admin| admin.into_sub_cross_account::<T>())
-				.collect::<Result<Vec<_>>>()?;
+		let admin_list = data
+			.admin_list
+			.into_iter()
+			.map(|admin| admin.into_sub_cross_account::<T>())
+			.collect::<Result<Vec<_>>>()?;
 
-			let flags = data.flags;
-			if !flags.is_allowed_for_user() {
-				return Err("internal flags were used".into());
-			}
+		let flags = data.flags;
+		if !flags.is_allowed_for_user() {
+			return Err("internal flags were used".into());
+		}
 
-			let data = CreateCollectionData {
-				name,
-				mode,
-				description,
-				token_prefix,
-				properties,
-				token_property_permissions,
-				limits,
-				pending_sponsor,
+		let data = CreateCollectionData {
+			name,
+			mode,
+			description,
+			token_prefix,
+			properties,
+			token_property_permissions,
+			limits,
+			pending_sponsor,
+			access: None,
+			permissions: Some(CollectionPermissions {
 				access: None,
-				permissions: Some(CollectionPermissions {
-					access: None,
-					mint_mode: None,
-					nesting: Some(NestingPermissions {
-						token_owner: data.nesting_settings.token_owner,
-						collection_admin: data.nesting_settings.collection_admin,
-						restricted,
-						#[cfg(feature = "runtime-benchmarks")]
-						permissive: true,
-					}),
+				mint_mode: None,
+				nesting: Some(NestingPermissions {
+					token_owner: data.nesting_settings.token_owner,
+					collection_admin: data.nesting_settings.collection_admin,
+					restricted,
+					#[cfg(feature = "runtime-benchmarks")]
+					permissive: true,
 				}),
-				admin_list,
-				flags,
-			};
-			check_sent_amount_equals_collection_creation_price::<T>(value)?;
-			let collection_helpers_address =
-				T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());
+			}),
+			admin_list,
+			flags,
+		};
+		check_sent_amount_equals_collection_creation_price::<T>(value)?;
+		let collection_helpers_address =
+			T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());
 
-			let collection_id = T::CollectionDispatch::create(caller, collection_helpers_address, data)
-				.map_err(dispatch_to_evm::<T>)?;
+		let collection_id = T::CollectionDispatch::create(caller, collection_helpers_address, data)
+			.map_err(dispatch_to_evm::<T>)?;
 
-			let address = pallet_common::eth::collection_id_to_address(collection_id);
-			Ok(address)
-		}
-	*/
+		let address = pallet_common::eth::collection_id_to_address(collection_id);
+		Ok(address)
+	}
 
 	/// Create an NFT collection
 	/// @param name Name of the collection
modifiedprimitives/data-structs/Cargo.tomldiffbeforeafterboth
--- a/primitives/data-structs/Cargo.toml
+++ b/primitives/data-structs/Cargo.toml
@@ -39,3 +39,4 @@
 	"sp-runtime/std",
 	"sp-std/std",
 ]
+stubgen = ["evm-coder/stubgen"]
modifiedruntime/common/config/pallets/mod.rsdiffbeforeafterboth
--- a/runtime/common/config/pallets/mod.rs
+++ b/runtime/common/config/pallets/mod.rs
@@ -24,7 +24,7 @@
 		weights::CommonWeights,
 		RelayChainBlockNumberProvider,
 	},
-	Runtime, RuntimeEvent, RuntimeCall, RuntimeOrigin, RUNTIME_NAME, TOKEN_SYMBOL, DECIMALS,
+	Runtime, RuntimeEvent, RuntimeCall, RUNTIME_NAME, TOKEN_SYMBOL, DECIMALS,
 	Balances,
 };
 use frame_support::traits::{ConstU32, ConstU64, Currency};
modifiedruntime/common/ethereum/sponsoring.rsdiffbeforeafterboth
--- a/runtime/common/ethereum/sponsoring.rs
+++ b/runtime/common/ethereum/sponsoring.rs
@@ -17,7 +17,7 @@
 //! Implements EVM sponsoring logic via TransactionValidityHack
 
 use core::{convert::TryInto, marker::PhantomData};
-use evm_coder::{Call, abi::AbiReader};
+use evm_coder::{Call};
 use pallet_common::{CollectionHandle, eth::map_eth_to_id};
 use pallet_evm::account::CrossAccountId;
 use pallet_evm_transaction_payment::CallContext;
@@ -66,11 +66,11 @@
 		if let Some(collection_id) = map_eth_to_id(&call_context.contract_address) {
 			let collection = <CollectionHandle<T>>::new(collection_id)?;
 			let sponsor = collection.sponsorship.sponsor()?.clone();
-			let (method_id, mut reader) = AbiReader::new_call(&call_context.input).ok()?;
+			// let (method_id, mut reader) = AbiReader::new_call(&call_context.input).ok()?;
 			Some(T::CrossAccountId::from_sub(match &collection.mode {
 				CollectionMode::NFT => {
 					let collection = NonfungibleHandle::cast(collection);
-					let call = <UniqueNFTCall<T>>::parse(method_id, &mut reader).ok()??;
+					let call = <UniqueNFTCall<T>>::parse_full(&call_context.input).ok()??;
 					match call {
 						UniqueNFTCall::TokenProperties(call) => match call {
 							TokenPropertiesCall::SetProperty {
@@ -161,11 +161,11 @@
 					}
 				}
 				CollectionMode::ReFungible => {
-					let call = <UniqueRefungibleCall<T>>::parse(method_id, &mut reader).ok()??;
+					let call = <UniqueRefungibleCall<T>>::parse_full(&call_context.input).ok()??;
 					refungible::call_sponsor(call, collection, who).map(|()| sponsor)
 				}
 				CollectionMode::Fungible(_) => {
-					let call = <UniqueFungibleCall<T>>::parse(method_id, &mut reader).ok()??;
+					let call = <UniqueFungibleCall<T>>::parse_full(&call_context.input).ok()??;
 					match call {
 						UniqueFungibleCall::ERC20(ERC20Call::Transfer { .. }) => {
 							withdraw_transfer::<T>(&collection, who, &TokenId::default())
@@ -196,8 +196,7 @@
 			// Token existance isn't checked at this point and should be checked in `withdraw` method.
 			let token = RefungibleTokenHandle(rft_collection, token_id);
 
-			let (method_id, mut reader) = AbiReader::new_call(&call_context.input).ok()?;
-			let call = <UniqueRefungibleTokenCall<T>>::parse(method_id, &mut reader).ok()??;
+			let call = <UniqueRefungibleTokenCall<T>>::parse_full(&call_context.input).ok()??;
 			Some(T::CrossAccountId::from_sub(
 				refungible::token_call_sponsor(call, token, who).map(|()| sponsor)?,
 			))
modifiedtests/src/eth/abi/collectionHelpers.jsondiffbeforeafterboth
--- a/tests/src/eth/abi/collectionHelpers.json
+++ b/tests/src/eth/abi/collectionHelpers.json
@@ -77,15 +77,6 @@
     "inputs": [
       {
         "components": [
-          {
-            "components": [
-              { "internalType": "address", "name": "eth", "type": "address" },
-              { "internalType": "uint256", "name": "sub", "type": "uint256" }
-            ],
-            "internalType": "struct CrossAddress",
-            "name": "pending_sponsor",
-            "type": "tuple"
-          },
           { "internalType": "string", "name": "name", "type": "string" },
           { "internalType": "string", "name": "description", "type": "string" },
           {
@@ -170,6 +161,15 @@
             "type": "tuple[]"
           },
           {
+            "components": [
+              { "internalType": "address", "name": "eth", "type": "address" },
+              { "internalType": "uint256", "name": "sub", "type": "uint256" }
+            ],
+            "internalType": "struct CrossAddress",
+            "name": "pending_sponsor",
+            "type": "tuple"
+          },
+          {
             "internalType": "CollectionFlags",
             "name": "flags",
             "type": "uint8"
modifiedtests/src/eth/util/playgrounds/types.tsdiffbeforeafterboth
--- a/tests/src/eth/util/playgrounds/types.ts
+++ b/tests/src/eth/util/playgrounds/types.ts
@@ -59,8 +59,8 @@
 }
 
 export enum CollectionMode {
+	Nonfungible,
 	Fungible,
-	Nonfungible,
 	Refungible,
 }
 
@@ -129,4 +129,4 @@
     else
       this.decimals = 0;
   }
-}
\ No newline at end of file
+}
modifiedtests/src/eth/util/playgrounds/unique.dev.tsdiffbeforeafterboth
--- a/tests/src/eth/util/playgrounds/unique.dev.ts
+++ b/tests/src/eth/util/playgrounds/unique.dev.ts
@@ -216,7 +216,6 @@
     }
 
     const tx = collectionHelper.methods.createCollection([
-      this.data.pendingSponsor,
       this.data.name,
       this.data.description,
       this.data.tokenPrefix,
@@ -227,6 +226,7 @@
       this.data.adminList,
       this.data.nestingSettings,
       this.data.limits,
+      this.data.pendingSponsor,
       this.data.flags,
     ]);
     return tx;