git.delta.rocks / unique-network / refs/commits / 78a995207d2e

difftreelog

fix After rebase

Trubnikov Sergey2022-08-29parent: #c288f09.patch.diff
in: master

8 files changed

modifiedcrates/evm-coder/src/abi.rsdiffbeforeafterboth
--- a/crates/evm-coder/src/abi.rs
+++ b/crates/evm-coder/src/abi.rs
@@ -28,10 +28,13 @@
 	types::{string, self},
 };
 use crate::execution::Result;
-use crate::solidity::SolidityTypeName;
 
 const ABI_ALIGNMENT: usize = 32;
 
+trait TypeHelper<T> {
+	fn is_dynamic() -> bool;
+}
+
 /// View into RLP data, which provides method to read typed items from it
 #[derive(Clone)]
 pub struct AbiReader<'i> {
@@ -330,11 +333,18 @@
 pub trait AbiRead<T> {
 	/// Read item from current position, advanding decoder
 	fn abi_read(&mut self) -> Result<T>;
+
+	/// Size for type aligned to [`ABI_ALIGNMENT`].
 	fn size() -> usize;
 }
 
 macro_rules! impl_abi_readable {
-	($ty:ty, $method:ident) => {
+	($ty:ty, $method:ident, $dynamic:literal) => {
+		impl TypeHelper<$ty> for $ty {
+			fn is_dynamic() -> bool {
+				$dynamic
+			}
+		}
 		impl AbiRead<$ty> for AbiReader<'_> {
 			fn abi_read(&mut self) -> Result<$ty> {
 				self.$method()
@@ -347,16 +357,16 @@
 	};
 }
 
-impl_abi_readable!(u8, uint8);
-impl_abi_readable!(u32, uint32);
-impl_abi_readable!(u64, uint64);
-impl_abi_readable!(u128, uint128);
-impl_abi_readable!(U256, uint256);
-impl_abi_readable!([u8; 4], bytes4);
-impl_abi_readable!(H160, address);
-impl_abi_readable!(Vec<u8>, bytes);
-impl_abi_readable!(bool, bool);
-impl_abi_readable!(string, string);
+impl_abi_readable!(u8, uint8, false);
+impl_abi_readable!(u32, uint32, false);
+impl_abi_readable!(u64, uint64, false);
+impl_abi_readable!(u128, uint128, false);
+impl_abi_readable!(U256, uint256, false);
+impl_abi_readable!([u8; 4], bytes4, false);
+impl_abi_readable!(H160, address, false);
+impl_abi_readable!(Vec<u8>, bytes, true);
+impl_abi_readable!(bool, bool, true);
+impl_abi_readable!(string, string, true);
 
 mod sealed {
 	/// Not all types can be placed in vec, i.e `Vec<u8>` is restricted, `bytes` should be used instead
@@ -389,16 +399,24 @@
 
 macro_rules! impl_tuples {
 	($($ident:ident)+) => {
+		impl<$($ident: TypeHelper<$ident>,)+> TypeHelper<($($ident,)+)> for ($($ident,)+) {
+			fn is_dynamic() -> bool {
+				false
+				$(
+					|| <$ident>::is_dynamic()
+				)*
+			}
+		}
 		impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {}
 		impl<$($ident),+> AbiRead<($($ident,)+)> for AbiReader<'_>
 		where
 			$(
 				Self: AbiRead<$ident>,
 			)+
-			($($ident,)+): SolidityTypeName,
+			($($ident,)+): TypeHelper<($($ident,)+)>,
 		{
 			fn abi_read(&mut self) -> Result<($($ident,)+)> {
-				let size = if <($($ident,)+)>::is_simple() { Some(<Self as AbiRead<($($ident,)+)>>::size()) } else { None };
+				let size = if !<($($ident,)+)>::is_dynamic() { Some(<Self as AbiRead<($($ident,)+)>>::size()) } else { None };
 				let mut subresult = self.subresult(size)?;
 				Ok((
 					$(<Self as AbiRead<$ident>>::abi_read(&mut subresult)?,)+
modifiedcrates/evm-coder/src/solidity.rsdiffbeforeafterboth
before · crates/evm-coder/src/solidity.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//! Implementation detail of [`crate::solidity_interface`] macro code-generation.18//! You should not rely on any public item from this module, as it is only intended to be used19//! by procedural macro, API and output format may be changed at any time.20//!21//! Purpose of this module is to receive solidity contract definition in module-specified22//! format, and then output string, representing interface of this contract in solidity language2324#[cfg(not(feature = "std"))]25use alloc::{string::String, vec::Vec, collections::BTreeMap, format};26#[cfg(feature = "std")]27use std::collections::BTreeMap;28use core::{29	fmt::{self, Write},30	marker::PhantomData,31	cell::{Cell, RefCell},32	cmp::Reverse,33};34use impl_trait_for_tuples::impl_for_tuples;35use crate::types::*;3637#[derive(Default)]38pub struct TypeCollector {39	/// Code => id40	/// id ordering is required to perform topo-sort on the resulting data41	structs: RefCell<BTreeMap<string, usize>>,42	anonymous: RefCell<BTreeMap<Vec<string>, usize>>,43	id: Cell<usize>,44}45impl TypeCollector {46	pub fn new() -> Self {47		Self::default()48	}49	pub fn collect(&self, item: string) {50		let id = self.next_id();51		self.structs.borrow_mut().insert(item, id);52	}53	pub fn next_id(&self) -> usize {54		let v = self.id.get();55		self.id.set(v + 1);56		v57	}58	pub fn collect_tuple<T: SolidityTupleType>(&self) -> String {59		let names = T::names(self);60		if let Some(id) = self.anonymous.borrow().get(&names).cloned() {61			return format!("Tuple{}", id);62		}63		let id = self.next_id();64		let mut str = String::new();65		writeln!(str, "/// @dev anonymous struct").unwrap();66		writeln!(str, "struct Tuple{} {{", id).unwrap();67		for (i, name) in names.iter().enumerate() {68			writeln!(str, "\t{} field_{};", name, i).unwrap();69		}70		writeln!(str, "}}").unwrap();71		self.collect(str);72		self.anonymous.borrow_mut().insert(names, id);73		format!("Tuple{}", id)74	}75	pub fn finish(self) -> Vec<string> {76		let mut data = self.structs.into_inner().into_iter().collect::<Vec<_>>();77		data.sort_by_key(|(_, id)| Reverse(*id));78		data.into_iter().map(|(code, _)| code).collect()79	}80}8182pub trait SolidityTypeName: 'static {83	fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;84	/// "simple" types are stored inline, no `memory` modifier should be used in solidity85	fn is_simple() -> bool;86	fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;87	/// Specialization88	fn is_void() -> bool {89		false90	}91}92macro_rules! solidity_type_name {93    ($($ty:ty => $name:literal $simple:literal = $default:literal),* $(,)?) => {94        $(95            impl SolidityTypeName for $ty {96                fn solidity_name(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {97                    write!(writer, $name)98                }99				fn is_simple() -> bool {100					$simple101				}102				fn solidity_default(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {103					write!(writer, $default)104				}105            }106        )*107    };108}109110solidity_type_name! {111	uint8 => "uint8" true = "0",112	uint32 => "uint32" true = "0",113	uint64 => "uint64" true = "0",114	uint128 => "uint128" true = "0",115	uint256 => "uint256" true = "0",116	bytes4 => "bytes4" true = "bytes4(0)",117	address => "address" true = "0x0000000000000000000000000000000000000000",118	string => "string" false = "\"\"",119	bytes => "bytes" false = "hex\"\"",120	bool => "bool" true = "false",121}122impl SolidityTypeName for void {123	fn solidity_name(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {124		Ok(())125	}126	fn is_simple() -> bool {127		true128	}129	fn solidity_default(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {130		Ok(())131	}132	fn is_void() -> bool {133		true134	}135}136137mod sealed {138	/// Not every type should be directly placed in vec.139	/// Vec encoding is not memory efficient, as every item will be padded140	/// to 32 bytes.141	/// Instead you should use specialized types (`bytes` in case of `Vec<u8>`)142	pub trait CanBePlacedInVec {}143}144145impl sealed::CanBePlacedInVec for uint256 {}146impl sealed::CanBePlacedInVec for string {}147impl sealed::CanBePlacedInVec for address {}148149impl<T: SolidityTypeName + sealed::CanBePlacedInVec> SolidityTypeName for Vec<T> {150	fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {151		T::solidity_name(writer, tc)?;152		write!(writer, "[]")153	}154	fn is_simple() -> bool {155		false156	}157	fn solidity_default(writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {158		write!(writer, "[]")159	}160}161162pub trait SolidityTupleType {163	fn names(tc: &TypeCollector) -> Vec<String>;164	fn len() -> usize;165}166167macro_rules! count {168    () => (0usize);169    ( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*));170}171172macro_rules! impl_tuples {173	($($ident:ident)+) => {174		impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {}175		impl<$($ident: SolidityTypeName + 'static),+> SolidityTupleType for ($($ident,)+) {176			fn names(tc: &TypeCollector) -> Vec<string> {177				let mut collected = Vec::with_capacity(Self::len());178				$({179					let mut out = string::new();180					$ident::solidity_name(&mut out, tc).expect("no fmt error");181					collected.push(out);182				})*;183				collected184			}185186			fn len() -> usize {187				count!($($ident)*)188			}189		}190		impl<$($ident: SolidityTypeName + 'static),+> SolidityTypeName for ($($ident,)+) {191			fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {192				write!(writer, "{}", tc.collect_tuple::<Self>())193			}194			fn is_simple() -> bool {195				true196				$(197					&& <$ident>::is_simple()198				)*199			}200			#[allow(unused_assignments)]201			fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {202				write!(writer, "{}(", tc.collect_tuple::<Self>())?;203				let mut first = true;204				$(205					if !first {206						write!(writer, ",")?;207					} else {208						first = false;209					}210					<$ident>::solidity_default(writer, tc)?;211				)*212				write!(writer, ")")213			}214		}215	};216}217218impl_tuples! {A}219impl_tuples! {A B}220impl_tuples! {A B C}221impl_tuples! {A B C D}222impl_tuples! {A B C D E}223impl_tuples! {A B C D E F}224impl_tuples! {A B C D E F G}225impl_tuples! {A B C D E F G H}226impl_tuples! {A B C D E F G H I}227impl_tuples! {A B C D E F G H I J}228229pub trait SolidityArguments {230	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;231	fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result;232	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;233	fn is_empty(&self) -> bool {234		self.len() == 0235	}236	fn len(&self) -> usize;237}238239#[derive(Default)]240pub struct UnnamedArgument<T>(PhantomData<*const T>);241242impl<T: SolidityTypeName> SolidityArguments for UnnamedArgument<T> {243	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {244		if !T::is_void() {245			T::solidity_name(writer, tc)?;246			if !T::is_simple() {247				write!(writer, " memory")?;248			}249			Ok(())250		} else {251			Ok(())252		}253	}254	fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {255		Ok(())256	}257	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {258		T::solidity_default(writer, tc)259	}260	fn len(&self) -> usize {261		if T::is_void() {262			0263		} else {264			1265		}266	}267}268269pub struct NamedArgument<T>(&'static str, PhantomData<*const T>);270271impl<T> NamedArgument<T> {272	pub fn new(name: &'static str) -> Self {273		Self(name, Default::default())274	}275}276277impl<T: SolidityTypeName> SolidityArguments for NamedArgument<T> {278	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {279		if !T::is_void() {280			T::solidity_name(writer, tc)?;281			if !T::is_simple() {282				write!(writer, " memory")?;283			}284			write!(writer, " {}", self.0)285		} else {286			Ok(())287		}288	}289	fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {290		writeln!(writer, "\t\t{};", self.0)291	}292	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {293		T::solidity_default(writer, tc)294	}295	fn len(&self) -> usize {296		if T::is_void() {297			0298		} else {299			1300		}301	}302}303304pub struct SolidityEventArgument<T>(pub bool, &'static str, PhantomData<*const T>);305306impl<T> SolidityEventArgument<T> {307	pub fn new(indexed: bool, name: &'static str) -> Self {308		Self(indexed, name, Default::default())309	}310}311312impl<T: SolidityTypeName> SolidityArguments for SolidityEventArgument<T> {313	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {314		if !T::is_void() {315			T::solidity_name(writer, tc)?;316			if self.0 {317				write!(writer, " indexed")?;318			}319			write!(writer, " {}", self.1)320		} else {321			Ok(())322		}323	}324	fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {325		writeln!(writer, "\t\t{};", self.1)326	}327	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {328		T::solidity_default(writer, tc)329	}330	fn len(&self) -> usize {331		if T::is_void() {332			0333		} else {334			1335		}336	}337}338339impl SolidityArguments for () {340	fn solidity_name(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {341		Ok(())342	}343	fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {344		Ok(())345	}346	fn solidity_default(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {347		Ok(())348	}349	fn len(&self) -> usize {350		0351	}352}353354#[impl_for_tuples(1, 12)]355impl SolidityArguments for Tuple {356	for_tuples!( where #( Tuple: SolidityArguments ),* );357358	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {359		let mut first = true;360		for_tuples!( #(361            if !Tuple.is_empty() {362                if !first {363                    write!(writer, ", ")?;364                }365                first = false;366                Tuple.solidity_name(writer, tc)?;367            }368        )* );369		Ok(())370	}371	fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {372		for_tuples!( #(373            Tuple.solidity_get(writer)?;374        )* );375		Ok(())376	}377	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {378		if self.is_empty() {379			Ok(())380		} else if self.len() == 1 {381			for_tuples!( #(382				Tuple.solidity_default(writer, tc)?;383			)* );384			Ok(())385		} else {386			write!(writer, "(")?;387			let mut first = true;388			for_tuples!( #(389				if !Tuple.is_empty() {390					if !first {391						write!(writer, ", ")?;392					}393					first = false;394					Tuple.solidity_default(writer, tc)?;395				}396			)* );397			write!(writer, ")")?;398			Ok(())399		}400	}401	fn len(&self) -> usize {402		for_tuples!( #( Tuple.len() )+* )403	}404}405406pub trait SolidityFunctions {407	fn solidity_name(408		&self,409		is_impl: bool,410		writer: &mut impl fmt::Write,411		tc: &TypeCollector,412	) -> fmt::Result;413}414415pub enum SolidityMutability {416	Pure,417	View,418	Mutable,419}420pub struct SolidityFunction<A, R> {421	pub docs: &'static [&'static str],422	pub selector_str: &'static str,423	pub selector: u32,424	pub name: &'static str,425	pub args: A,426	pub result: R,427	pub mutability: SolidityMutability,428}429impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {430	fn solidity_name(431		&self,432		is_impl: bool,433		writer: &mut impl fmt::Write,434		tc: &TypeCollector,435	) -> fmt::Result {436		for doc in self.docs {437			writeln!(writer, "\t///{}", doc)?;438		}439		writeln!(440			writer,441			"\t/// @dev EVM selector for this function is: 0x{:0>8x},",442			self.selector443		)?;444		writeln!(writer, "\t///  or in textual repr: {}", self.selector_str)?;445		write!(writer, "\tfunction {}(", self.name)?;446		self.args.solidity_name(writer, tc)?;447		write!(writer, ")")?;448		if is_impl {449			write!(writer, " public")?;450		} else {451			write!(writer, " external")?;452		}453		match &self.mutability {454			SolidityMutability::Pure => write!(writer, " pure")?,455			SolidityMutability::View => write!(writer, " view")?,456			SolidityMutability::Mutable => {}457		}458		if !self.result.is_empty() {459			write!(writer, " returns (")?;460			self.result.solidity_name(writer, tc)?;461			write!(writer, ")")?;462		}463		if is_impl {464			writeln!(writer, " {{")?;465			writeln!(writer, "\t\trequire(false, stub_error);")?;466			self.args.solidity_get(writer)?;467			match &self.mutability {468				SolidityMutability::Pure => {}469				SolidityMutability::View => writeln!(writer, "\t\tdummy;")?,470				SolidityMutability::Mutable => writeln!(writer, "\t\tdummy = 0;")?,471			}472			if !self.result.is_empty() {473				write!(writer, "\t\treturn ")?;474				self.result.solidity_default(writer, tc)?;475				writeln!(writer, ";")?;476			}477			writeln!(writer, "\t}}")?;478		} else {479			writeln!(writer, ";")?;480		}481		Ok(())482	}483}484485#[impl_for_tuples(0, 48)]486impl SolidityFunctions for Tuple {487	for_tuples!( where #( Tuple: SolidityFunctions ),* );488489	fn solidity_name(490		&self,491		is_impl: bool,492		writer: &mut impl fmt::Write,493		tc: &TypeCollector,494	) -> fmt::Result {495		let mut first = false;496		for_tuples!( #(497            Tuple.solidity_name(is_impl, writer, tc)?;498        )* );499		Ok(())500	}501}502503pub struct SolidityInterface<F: SolidityFunctions> {504	pub docs: &'static [&'static str],505	pub selector: bytes4,506	pub name: &'static str,507	pub is: &'static [&'static str],508	pub functions: F,509}510511impl<F: SolidityFunctions> SolidityInterface<F> {512	pub fn format(513		&self,514		is_impl: bool,515		out: &mut impl fmt::Write,516		tc: &TypeCollector,517	) -> fmt::Result {518		const ZERO_BYTES: [u8; 4] = [0; 4];519		for doc in self.docs {520			writeln!(out, "///{}", doc)?;521		}522		if self.selector != ZERO_BYTES {523			writeln!(524				out,525				"/// @dev the ERC-165 identifier for this interface is 0x{:0>8x}",526				u32::from_be_bytes(self.selector)527			)?;528		}529		if is_impl {530			write!(out, "contract ")?;531		} else {532			write!(out, "interface ")?;533		}534		write!(out, "{}", self.name)?;535		if !self.is.is_empty() {536			write!(out, " is")?;537			for (i, n) in self.is.iter().enumerate() {538				if i != 0 {539					write!(out, ",")?;540				}541				write!(out, " {}", n)?;542			}543		}544		writeln!(out, " {{")?;545		self.functions.solidity_name(is_impl, out, tc)?;546		writeln!(out, "}}")?;547		Ok(())548	}549}550551pub struct SolidityEvent<A> {552	pub name: &'static str,553	pub args: A,554}555556impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {557	fn solidity_name(558		&self,559		_is_impl: bool,560		writer: &mut impl fmt::Write,561		tc: &TypeCollector,562	) -> fmt::Result {563		write!(writer, "\tevent {}(", self.name)?;564		self.args.solidity_name(writer, tc)?;565		writeln!(writer, ");")566	}567}
after · crates/evm-coder/src/solidity.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//! Implementation detail of [`crate::solidity_interface`] macro code-generation.18//! You should not rely on any public item from this module, as it is only intended to be used19//! by procedural macro, API and output format may be changed at any time.20//!21//! Purpose of this module is to receive solidity contract definition in module-specified22//! format, and then output string, representing interface of this contract in solidity language2324#[cfg(not(feature = "std"))]25use alloc::{string::String, vec::Vec, collections::BTreeMap, format};26#[cfg(feature = "std")]27use std::collections::BTreeMap;28use core::{29	fmt::{self, Write},30	marker::PhantomData,31	cell::{Cell, RefCell},32	cmp::Reverse,33};34use impl_trait_for_tuples::impl_for_tuples;35use crate::types::*;3637#[derive(Default)]38pub struct TypeCollector {39	/// Code => id40	/// id ordering is required to perform topo-sort on the resulting data41	structs: RefCell<BTreeMap<string, usize>>,42	anonymous: RefCell<BTreeMap<Vec<string>, usize>>,43	id: Cell<usize>,44}45impl TypeCollector {46	pub fn new() -> Self {47		Self::default()48	}49	pub fn collect(&self, item: string) {50		let id = self.next_id();51		self.structs.borrow_mut().insert(item, id);52	}53	pub fn next_id(&self) -> usize {54		let v = self.id.get();55		self.id.set(v + 1);56		v57	}58	pub fn collect_tuple<T: SolidityTupleType>(&self) -> String {59		let names = T::names(self);60		if let Some(id) = self.anonymous.borrow().get(&names).cloned() {61			return format!("Tuple{}", id);62		}63		let id = self.next_id();64		let mut str = String::new();65		writeln!(str, "/// @dev anonymous struct").unwrap();66		writeln!(str, "struct Tuple{} {{", id).unwrap();67		for (i, name) in names.iter().enumerate() {68			writeln!(str, "\t{} field_{};", name, i).unwrap();69		}70		writeln!(str, "}}").unwrap();71		self.collect(str);72		self.anonymous.borrow_mut().insert(names, id);73		format!("Tuple{}", id)74	}75	pub fn finish(self) -> Vec<string> {76		let mut data = self.structs.into_inner().into_iter().collect::<Vec<_>>();77		data.sort_by_key(|(_, id)| Reverse(*id));78		data.into_iter().map(|(code, _)| code).collect()79	}80}8182pub trait SolidityTypeName: 'static {83	fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;84	/// "simple" types are stored inline, no `memory` modifier should be used in solidity85	fn is_simple() -> bool;86	fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;87	/// Specialization88	fn is_void() -> bool {89		false90	}91}92macro_rules! solidity_type_name {93    ($($ty:ty => $name:literal $simple:literal = $default:literal),* $(,)?) => {94        $(95            impl SolidityTypeName for $ty {96                fn solidity_name(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {97                    write!(writer, $name)98                }99				fn is_simple() -> bool {100					$simple101				}102				fn solidity_default(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {103					write!(writer, $default)104				}105            }106        )*107    };108}109110solidity_type_name! {111	uint8 => "uint8" true = "0",112	uint32 => "uint32" true = "0",113	uint64 => "uint64" true = "0",114	uint128 => "uint128" true = "0",115	uint256 => "uint256" true = "0",116	bytes4 => "bytes4" true = "bytes4(0)",117	address => "address" true = "0x0000000000000000000000000000000000000000",118	string => "string" false = "\"\"",119	bytes => "bytes" false = "hex\"\"",120	bool => "bool" true = "false",121}122impl SolidityTypeName for void {123	fn solidity_name(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {124		Ok(())125	}126	fn is_simple() -> bool {127		true128	}129	fn solidity_default(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {130		Ok(())131	}132	fn is_void() -> bool {133		true134	}135}136137mod sealed {138	/// Not every type should be directly placed in vec.139	/// Vec encoding is not memory efficient, as every item will be padded140	/// to 32 bytes.141	/// Instead you should use specialized types (`bytes` in case of `Vec<u8>`)142	pub trait CanBePlacedInVec {}143}144145impl sealed::CanBePlacedInVec for uint256 {}146impl sealed::CanBePlacedInVec for string {}147impl sealed::CanBePlacedInVec for address {}148149impl<T: SolidityTypeName + sealed::CanBePlacedInVec> SolidityTypeName for Vec<T> {150	fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {151		T::solidity_name(writer, tc)?;152		write!(writer, "[]")153	}154	fn is_simple() -> bool {155		false156	}157	fn solidity_default(writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {158		write!(writer, "[]")159	}160}161162pub trait SolidityTupleType {163	fn names(tc: &TypeCollector) -> Vec<String>;164	fn len() -> usize;165}166167macro_rules! count {168    () => (0usize);169    ( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*));170}171172macro_rules! impl_tuples {173	($($ident:ident)+) => {174		impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {}175		impl<$($ident: SolidityTypeName + 'static),+> SolidityTupleType for ($($ident,)+) {176			fn names(tc: &TypeCollector) -> Vec<string> {177				let mut collected = Vec::with_capacity(Self::len());178				$({179					let mut out = string::new();180					$ident::solidity_name(&mut out, tc).expect("no fmt error");181					collected.push(out);182				})*;183				collected184			}185186			fn len() -> usize {187				count!($($ident)*)188			}189		}190		impl<$($ident: SolidityTypeName + 'static),+> SolidityTypeName for ($($ident,)+) {191			fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {192				write!(writer, "{}", tc.collect_tuple::<Self>())193			}194			fn is_simple() -> bool {195				false196			}197			#[allow(unused_assignments)]198			fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {199				write!(writer, "{}(", tc.collect_tuple::<Self>())?;200				let mut first = true;201				$(202					if !first {203						write!(writer, ",")?;204					} else {205						first = false;206					}207					<$ident>::solidity_default(writer, tc)?;208				)*209				write!(writer, ")")210			}211		}212	};213}214215impl_tuples! {A}216impl_tuples! {A B}217impl_tuples! {A B C}218impl_tuples! {A B C D}219impl_tuples! {A B C D E}220impl_tuples! {A B C D E F}221impl_tuples! {A B C D E F G}222impl_tuples! {A B C D E F G H}223impl_tuples! {A B C D E F G H I}224impl_tuples! {A B C D E F G H I J}225226pub trait SolidityArguments {227	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;228	fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result;229	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;230	fn is_empty(&self) -> bool {231		self.len() == 0232	}233	fn len(&self) -> usize;234}235236#[derive(Default)]237pub struct UnnamedArgument<T>(PhantomData<*const T>);238239impl<T: SolidityTypeName> SolidityArguments for UnnamedArgument<T> {240	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {241		if !T::is_void() {242			T::solidity_name(writer, tc)?;243			if !T::is_simple() {244				write!(writer, " memory")?;245			}246			Ok(())247		} else {248			Ok(())249		}250	}251	fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {252		Ok(())253	}254	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {255		T::solidity_default(writer, tc)256	}257	fn len(&self) -> usize {258		if T::is_void() {259			0260		} else {261			1262		}263	}264}265266pub struct NamedArgument<T>(&'static str, PhantomData<*const T>);267268impl<T> NamedArgument<T> {269	pub fn new(name: &'static str) -> Self {270		Self(name, Default::default())271	}272}273274impl<T: SolidityTypeName> SolidityArguments for NamedArgument<T> {275	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {276		if !T::is_void() {277			T::solidity_name(writer, tc)?;278			if !T::is_simple() {279				write!(writer, " memory")?;280			}281			write!(writer, " {}", self.0)282		} else {283			Ok(())284		}285	}286	fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {287		writeln!(writer, "\t\t{};", self.0)288	}289	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {290		T::solidity_default(writer, tc)291	}292	fn len(&self) -> usize {293		if T::is_void() {294			0295		} else {296			1297		}298	}299}300301pub struct SolidityEventArgument<T>(pub bool, &'static str, PhantomData<*const T>);302303impl<T> SolidityEventArgument<T> {304	pub fn new(indexed: bool, name: &'static str) -> Self {305		Self(indexed, name, Default::default())306	}307}308309impl<T: SolidityTypeName> SolidityArguments for SolidityEventArgument<T> {310	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {311		if !T::is_void() {312			T::solidity_name(writer, tc)?;313			if self.0 {314				write!(writer, " indexed")?;315			}316			write!(writer, " {}", self.1)317		} else {318			Ok(())319		}320	}321	fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {322		writeln!(writer, "\t\t{};", self.1)323	}324	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {325		T::solidity_default(writer, tc)326	}327	fn len(&self) -> usize {328		if T::is_void() {329			0330		} else {331			1332		}333	}334}335336impl SolidityArguments for () {337	fn solidity_name(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {338		Ok(())339	}340	fn solidity_get(&self, _writer: &mut impl fmt::Write) -> fmt::Result {341		Ok(())342	}343	fn solidity_default(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {344		Ok(())345	}346	fn len(&self) -> usize {347		0348	}349}350351#[impl_for_tuples(1, 12)]352impl SolidityArguments for Tuple {353	for_tuples!( where #( Tuple: SolidityArguments ),* );354355	fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {356		let mut first = true;357		for_tuples!( #(358            if !Tuple.is_empty() {359                if !first {360                    write!(writer, ", ")?;361                }362                first = false;363                Tuple.solidity_name(writer, tc)?;364            }365        )* );366		Ok(())367	}368	fn solidity_get(&self, writer: &mut impl fmt::Write) -> fmt::Result {369		for_tuples!( #(370            Tuple.solidity_get(writer)?;371        )* );372		Ok(())373	}374	fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {375		if self.is_empty() {376			Ok(())377		} else if self.len() == 1 {378			for_tuples!( #(379				Tuple.solidity_default(writer, tc)?;380			)* );381			Ok(())382		} else {383			write!(writer, "(")?;384			let mut first = true;385			for_tuples!( #(386				if !Tuple.is_empty() {387					if !first {388						write!(writer, ", ")?;389					}390					first = false;391					Tuple.solidity_default(writer, tc)?;392				}393			)* );394			write!(writer, ")")?;395			Ok(())396		}397	}398	fn len(&self) -> usize {399		for_tuples!( #( Tuple.len() )+* )400	}401}402403pub trait SolidityFunctions {404	fn solidity_name(405		&self,406		is_impl: bool,407		writer: &mut impl fmt::Write,408		tc: &TypeCollector,409	) -> fmt::Result;410}411412pub enum SolidityMutability {413	Pure,414	View,415	Mutable,416}417pub struct SolidityFunction<A, R> {418	pub docs: &'static [&'static str],419	pub selector_str: &'static str,420	pub selector: u32,421	pub name: &'static str,422	pub args: A,423	pub result: R,424	pub mutability: SolidityMutability,425}426impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {427	fn solidity_name(428		&self,429		is_impl: bool,430		writer: &mut impl fmt::Write,431		tc: &TypeCollector,432	) -> fmt::Result {433		for doc in self.docs {434			writeln!(writer, "\t///{}", doc)?;435		}436		writeln!(437			writer,438			"\t/// @dev EVM selector for this function is: 0x{:0>8x},",439			self.selector440		)?;441		writeln!(writer, "\t///  or in textual repr: {}", self.selector_str)?;442		write!(writer, "\tfunction {}(", self.name)?;443		self.args.solidity_name(writer, tc)?;444		write!(writer, ")")?;445		if is_impl {446			write!(writer, " public")?;447		} else {448			write!(writer, " external")?;449		}450		match &self.mutability {451			SolidityMutability::Pure => write!(writer, " pure")?,452			SolidityMutability::View => write!(writer, " view")?,453			SolidityMutability::Mutable => {}454		}455		if !self.result.is_empty() {456			write!(writer, " returns (")?;457			self.result.solidity_name(writer, tc)?;458			write!(writer, ")")?;459		}460		if is_impl {461			writeln!(writer, " {{")?;462			writeln!(writer, "\t\trequire(false, stub_error);")?;463			self.args.solidity_get(writer)?;464			match &self.mutability {465				SolidityMutability::Pure => {}466				SolidityMutability::View => writeln!(writer, "\t\tdummy;")?,467				SolidityMutability::Mutable => writeln!(writer, "\t\tdummy = 0;")?,468			}469			if !self.result.is_empty() {470				write!(writer, "\t\treturn ")?;471				self.result.solidity_default(writer, tc)?;472				writeln!(writer, ";")?;473			}474			writeln!(writer, "\t}}")?;475		} else {476			writeln!(writer, ";")?;477		}478		Ok(())479	}480}481482#[impl_for_tuples(0, 48)]483impl SolidityFunctions for Tuple {484	for_tuples!( where #( Tuple: SolidityFunctions ),* );485486	fn solidity_name(487		&self,488		is_impl: bool,489		writer: &mut impl fmt::Write,490		tc: &TypeCollector,491	) -> fmt::Result {492		let mut first = false;493		for_tuples!( #(494            Tuple.solidity_name(is_impl, writer, tc)?;495        )* );496		Ok(())497	}498}499500pub struct SolidityInterface<F: SolidityFunctions> {501	pub docs: &'static [&'static str],502	pub selector: bytes4,503	pub name: &'static str,504	pub is: &'static [&'static str],505	pub functions: F,506}507508impl<F: SolidityFunctions> SolidityInterface<F> {509	pub fn format(510		&self,511		is_impl: bool,512		out: &mut impl fmt::Write,513		tc: &TypeCollector,514	) -> fmt::Result {515		const ZERO_BYTES: [u8; 4] = [0; 4];516		for doc in self.docs {517			writeln!(out, "///{}", doc)?;518		}519		if self.selector != ZERO_BYTES {520			writeln!(521				out,522				"/// @dev the ERC-165 identifier for this interface is 0x{:0>8x}",523				u32::from_be_bytes(self.selector)524			)?;525		}526		if is_impl {527			write!(out, "contract ")?;528		} else {529			write!(out, "interface ")?;530		}531		write!(out, "{}", self.name)?;532		if !self.is.is_empty() {533			write!(out, " is")?;534			for (i, n) in self.is.iter().enumerate() {535				if i != 0 {536					write!(out, ",")?;537				}538				write!(out, " {}", n)?;539			}540		}541		writeln!(out, " {{")?;542		self.functions.solidity_name(is_impl, out, tc)?;543		writeln!(out, "}}")?;544		Ok(())545	}546}547548pub struct SolidityEvent<A> {549	pub name: &'static str,550	pub args: A,551}552553impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {554	fn solidity_name(555		&self,556		_is_impl: bool,557		writer: &mut impl fmt::Write,558		tc: &TypeCollector,559	) -> fmt::Result {560		write!(writer, "\tevent {}(", self.name)?;561		self.args.solidity_name(writer, tc)?;562		writeln!(writer, ");")563	}564}
modifiedpallets/fungible/src/erc.rsdiffbeforeafterboth
--- a/pallets/fungible/src/erc.rs
+++ b/pallets/fungible/src/erc.rs
@@ -129,7 +129,7 @@
 	}
 }
 
-#[solidity_interface(name = "ERC20Mintable")]
+#[solidity_interface(name = ERC20Mintable)]
 impl<T: Config> FungibleHandle<T> {
 	/// Mint tokens for `to` account.
 	/// @param to account that will receive minted tokens
@@ -148,7 +148,7 @@
 	}
 }
 
-#[solidity_interface(name = "ERC20UniqueExtensions")]
+#[solidity_interface(name = ERC20UniqueExtensions)]
 impl<T: Config> FungibleHandle<T> {
 	/// Burn tokens from account
 	/// @dev Function that burns an `amount` of the tokens of a given account,
modifiedpallets/fungible/src/stubs/UniqueFungible.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/fungible/src/stubs/UniqueFungible.soldiffbeforeafterboth
--- a/pallets/fungible/src/stubs/UniqueFungible.sol
+++ b/pallets/fungible/src/stubs/UniqueFungible.sol
@@ -3,13 +3,7 @@
 
 pragma solidity >=0.8.0 <0.9.0;
 
-// Anonymous struct
-struct Tuple0 {
-	address field_0;
-	uint256 field_1;
-}
-
-// Common stubs holder
+/// @dev common stubs holder
 contract Dummy {
 	uint8 dummy;
 	string stub_error = "this contract is implemented in native";
@@ -27,49 +21,8 @@
 	}
 }
 
-// Inline
-contract ERC20Events {
-	event Transfer(address indexed from, address indexed to, uint256 value);
-	event Approval(
-		address indexed owner,
-		address indexed spender,
-		uint256 value
-	);
-}
-
-// Selector: 40c10f19
-contract ERC20Mintable is Dummy, ERC165 {
-	// Selector: mint(address,uint256) 40c10f19
-	function mint(address to, uint256 amount) public returns (bool) {
-		require(false, stub_error);
-		to;
-		amount;
-		dummy = 0;
-		return false;
-	}
-}
-
-// Selector: 63034ac5
-contract ERC20UniqueExtensions is Dummy, ERC165 {
-	// Selector: burnFrom(address,uint256) 79cc6790
-	function burnFrom(address from, uint256 amount) public returns (bool) {
-		require(false, stub_error);
-		from;
-		amount;
-		dummy = 0;
-		return false;
-	}
-
-	// Selector: mintBulk((address,uint256)[]) 1acf2d55
-	function mintBulk(Tuple0[] memory amounts) public returns (bool) {
-		require(false, stub_error);
-		amounts;
-		dummy = 0;
-		return false;
-	}
-}
-
-// Selector: 6cf113cd
+/// @title A contract that allows you to work with collections.
+/// @dev the ERC-165 identifier for this interface is 0xe54be640
 contract Collection is Dummy, ERC165 {
 	/// Set collection property.
 	///
@@ -397,19 +350,51 @@
 	}
 }
 
+/// @dev the ERC-165 identifier for this interface is 0x63034ac5
+contract ERC20UniqueExtensions is Dummy, ERC165 {
+	/// Burn tokens from account
+	/// @dev Function that burns an `amount` of the tokens of a given account,
+	/// deducting from the sender's allowance for said account.
+	/// @param from The account whose tokens will be burnt.
+	/// @param amount The amount that will be burnt.
+	/// @dev EVM selector for this function is: 0x79cc6790,
+	///  or in textual repr: burnFrom(address,uint256)
+	function burnFrom(address from, uint256 amount) public returns (bool) {
+		require(false, stub_error);
+		from;
+		amount;
+		dummy = 0;
+		return false;
+	}
+
+	/// Mint tokens for multiple accounts.
+	/// @param amounts array of pairs of account address and amount
+	/// @dev EVM selector for this function is: 0x1acf2d55,
+	///  or in textual repr: mintBulk((address,uint256)[])
+	function mintBulk(Tuple6[] memory amounts) public returns (bool) {
+		require(false, stub_error);
+		amounts;
+		dummy = 0;
+		return false;
+	}
+}
+
 /// @dev anonymous struct
 struct Tuple6 {
 	address field_0;
 	uint256 field_1;
 }
 
-/// @dev the ERC-165 identifier for this interface is 0x79cc6790
-contract ERC20UniqueExtensions is Dummy, ERC165 {
-	/// @dev EVM selector for this function is: 0x79cc6790,
-	///  or in textual repr: burnFrom(address,uint256)
-	function burnFrom(address from, uint256 amount) public returns (bool) {
+/// @dev the ERC-165 identifier for this interface is 0x40c10f19
+contract ERC20Mintable is Dummy, ERC165 {
+	/// Mint tokens for `to` account.
+	/// @param to account that will receive minted tokens
+	/// @param amount amount of tokens to mint
+	/// @dev EVM selector for this function is: 0x40c10f19,
+	///  or in textual repr: mint(address,uint256)
+	function mint(address to, uint256 amount) public returns (bool) {
 		require(false, stub_error);
-		from;
+		to;
 		amount;
 		dummy = 0;
 		return false;
modifiedtests/src/eth/api/UniqueFungible.soldiffbeforeafterboth
--- a/tests/src/eth/api/UniqueFungible.sol
+++ b/tests/src/eth/api/UniqueFungible.sol
@@ -3,13 +3,7 @@
 
 pragma solidity >=0.8.0 <0.9.0;
 
-// Anonymous struct
-struct Tuple0 {
-	address field_0;
-	uint256 field_1;
-}
-
-// Common stubs holder
+/// @dev common stubs holder
 interface Dummy {
 
 }
@@ -18,32 +12,8 @@
 	function supportsInterface(bytes4 interfaceID) external view returns (bool);
 }
 
-// Inline
-interface ERC20Events {
-	event Transfer(address indexed from, address indexed to, uint256 value);
-	event Approval(
-		address indexed owner,
-		address indexed spender,
-		uint256 value
-	);
-}
-
-// Selector: 40c10f19
-interface ERC20Mintable is Dummy, ERC165 {
-	// Selector: mint(address,uint256) 40c10f19
-	function mint(address to, uint256 amount) external returns (bool);
-}
-
-// Selector: 63034ac5
-interface ERC20UniqueExtensions is Dummy, ERC165 {
-	// Selector: burnFrom(address,uint256) 79cc6790
-	function burnFrom(address from, uint256 amount) external returns (bool);
-
-	// Selector: mintBulk((address,uint256)[]) 1acf2d55
-	function mintBulk(Tuple0[] memory amounts) external returns (bool);
-}
-
-// Selector: 6cf113cd
+/// @title A contract that allows you to work with collections.
+/// @dev the ERC-165 identifier for this interface is 0xe54be640
 interface Collection is Dummy, ERC165 {
 	/// Set collection property.
 	///
@@ -237,13 +207,56 @@
 	/// @dev EVM selector for this function is: 0xd34b55b8,
 	///  or in textual repr: uniqueCollectionType()
 	function uniqueCollectionType() external returns (string memory);
+
+	/// Changes collection owner to another account
+	///
+	/// @dev Owner can be changed only by current owner
+	/// @param newOwner new owner account
+	/// @dev EVM selector for this function is: 0x13af4035,
+	///  or in textual repr: setOwner(address)
+	function setOwner(address newOwner) external;
+
+	/// Changes collection owner to another substrate account
+	///
+	/// @dev Owner can be changed only by current owner
+	/// @param newOwner new owner substrate account
+	/// @dev EVM selector for this function is: 0xb212138f,
+	///  or in textual repr: setOwnerSubstrate(uint256)
+	function setOwnerSubstrate(uint256 newOwner) external;
 }
 
-/// @dev the ERC-165 identifier for this interface is 0x79cc6790
+/// @dev the ERC-165 identifier for this interface is 0x63034ac5
 interface ERC20UniqueExtensions is Dummy, ERC165 {
+	/// Burn tokens from account
+	/// @dev Function that burns an `amount` of the tokens of a given account,
+	/// deducting from the sender's allowance for said account.
+	/// @param from The account whose tokens will be burnt.
+	/// @param amount The amount that will be burnt.
 	/// @dev EVM selector for this function is: 0x79cc6790,
 	///  or in textual repr: burnFrom(address,uint256)
 	function burnFrom(address from, uint256 amount) external returns (bool);
+
+	/// Mint tokens for multiple accounts.
+	/// @param amounts array of pairs of account address and amount
+	/// @dev EVM selector for this function is: 0x1acf2d55,
+	///  or in textual repr: mintBulk((address,uint256)[])
+	function mintBulk(Tuple6[] memory amounts) external returns (bool);
+}
+
+/// @dev anonymous struct
+struct Tuple6 {
+	address field_0;
+	uint256 field_1;
+}
+
+/// @dev the ERC-165 identifier for this interface is 0x40c10f19
+interface ERC20Mintable is Dummy, ERC165 {
+	/// Mint tokens for `to` account.
+	/// @param to account that will receive minted tokens
+	/// @param amount amount of tokens to mint
+	/// @dev EVM selector for this function is: 0x40c10f19,
+	///  or in textual repr: mint(address,uint256)
+	function mint(address to, uint256 amount) external returns (bool);
 }
 
 /// @dev inlined interface
modifiedtests/src/eth/base.test.tsdiffbeforeafterboth
--- a/tests/src/eth/base.test.ts
+++ b/tests/src/eth/base.test.ts
@@ -94,7 +94,7 @@
   });
 
   itWeb3('ERC721 support', async ({web3}) => {
-    expect(await contract(web3).methods.supportsInterface('0x58800161').call()).to.be.true;
+    expect(await contract(web3).methods.supportsInterface('0x780e9d63').call()).to.be.true;
   });
 
   itWeb3('ERC721Metadata support', async ({web3}) => {
modifiedtests/src/eth/fungibleAbi.jsondiffbeforeafterboth
--- a/tests/src/eth/fungibleAbi.json
+++ b/tests/src/eth/fungibleAbi.json
@@ -151,33 +151,6 @@
     "type": "function"
   },
   {
-    "inputs": [
-      { "internalType": "address", "name": "to", "type": "address" },
-      { "internalType": "uint256", "name": "amount", "type": "uint256" }
-    ],
-    "name": "mint",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
-    "inputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "field_0", "type": "address" },
-          { "internalType": "uint256", "name": "field_1", "type": "uint256" }
-        ],
-        "internalType": "struct Tuple0[]",
-        "name": "amounts",
-        "type": "tuple[]"
-      }
-    ],
-    "name": "mintBulk",
-    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
-    "stateMutability": "nonpayable",
-    "type": "function"
-  },
-  {
     "inputs": [],
     "name": "getCollectionSponsor",
     "outputs": [
@@ -220,6 +193,33 @@
     "type": "function"
   },
   {
+    "inputs": [
+      { "internalType": "address", "name": "to", "type": "address" },
+      { "internalType": "uint256", "name": "amount", "type": "uint256" }
+    ],
+    "name": "mint",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "field_0", "type": "address" },
+          { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+        ],
+        "internalType": "struct Tuple6[]",
+        "name": "amounts",
+        "type": "tuple[]"
+      }
+    ],
+    "name": "mintBulk",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
     "inputs": [],
     "name": "name",
     "outputs": [{ "internalType": "string", "name": "", "type": "string" }],