difftreelog
Merge branch 'develop' into tests/eth-helpers
in: master
59 files changed
.maintain/scripts/generate_abi.shdiffbeforeafterboth--- a/.maintain/scripts/generate_abi.sh
+++ b/.maintain/scripts/generate_abi.sh
@@ -4,6 +4,7 @@
dir=$PWD
tmp=$(mktemp -d)
+echo "Tmp file: $tmp/input.sol"
cd $tmp
cp $dir/$INPUT input.sol
solcjs --abi -p input.sol
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1058,16 +1058,6 @@
]
[[package]]
-name = "concat-idents"
-version = "1.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fe0e1d9f7de897d18e590a7496b5facbe87813f746cf4b8db596ba77e07e832"
-dependencies = [
- "quote",
- "syn",
-]
-
-[[package]]
name = "concurrent-queue"
version = "1.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2347,9 +2337,8 @@
[[package]]
name = "evm-coder"
-version = "0.1.4"
+version = "0.1.5"
dependencies = [
- "concat-idents",
"ethereum",
"evm-coder-procedural",
"evm-core",
@@ -2367,7 +2356,7 @@
[[package]]
name = "evm-coder-procedural"
-version = "0.2.1"
+version = "0.2.2"
dependencies = [
"Inflector",
"hex",
crates/evm-coder/CHANGELOG.mddiffbeforeafterboth--- a/crates/evm-coder/CHANGELOG.md
+++ b/crates/evm-coder/CHANGELOG.md
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
<!-- bureaucrate goes here -->
+## [v0.1.5] - 2022-11-30
+
+### Added
+- Derive macro to support structures and enums.
## [v0.1.4] - 2022-11-02
crates/evm-coder/Cargo.tomldiffbeforeafterboth--- a/crates/evm-coder/Cargo.toml
+++ b/crates/evm-coder/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "evm-coder"
-version = "0.1.4"
+version = "0.1.5"
license = "GPLv3"
edition = "2021"
@@ -26,7 +26,6 @@
hex = "0.4.3"
hex-literal = "0.3.4"
similar-asserts = "1.4.2"
-concat-idents = "1.1.3"
trybuild = "1.0"
[features]
crates/evm-coder/procedural/Cargo.tomldiffbeforeafterboth--- a/crates/evm-coder/procedural/Cargo.toml
+++ b/crates/evm-coder/procedural/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "evm-coder-procedural"
-version = "0.2.1"
+version = "0.2.2"
license = "GPLv3"
edition = "2021"
crates/evm-coder/procedural/src/abi_derive/derive_enum.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/evm-coder/procedural/src/abi_derive/derive_enum.rs
@@ -0,0 +1,211 @@
+use quote::quote;
+
+pub fn impl_solidity_option<'a>(
+ name: &proc_macro2::Ident,
+ enum_options: impl Iterator<Item = &'a syn::Ident>,
+) -> proc_macro2::TokenStream {
+ let enum_options = enum_options.map(|opt| {
+ let s = name.to_string() + "." + opt.to_string().as_str();
+ let as_string = proc_macro2::Literal::string(s.as_str());
+ quote!(#name::#opt => #as_string,)
+ });
+ quote!(
+ #[cfg(feature = "stubgen")]
+ impl ::evm_coder::solidity::SolidityEnum for #name {
+ fn solidity_option(&self) -> &str {
+ match self {
+ #(#enum_options)*
+ }
+ }
+ }
+ )
+}
+
+pub fn impl_enum_from_u8<'a>(
+ name: &proc_macro2::Ident,
+ enum_options: impl Iterator<Item = &'a syn::Ident>,
+) -> proc_macro2::TokenStream {
+ let error_str = format!("Value not convertible into enum \"{name}\"");
+ let error_str = proc_macro2::Literal::string(&error_str);
+ let enum_options = enum_options.enumerate().map(|(i, opt)| {
+ let n = proc_macro2::Literal::u8_suffixed(i as u8);
+ quote! {#n => Ok(#name::#opt),}
+ });
+
+ quote!(
+ impl TryFrom<u8> for #name {
+ type Error = &'static str;
+
+ fn try_from(value: u8) -> ::core::result::Result<Self, Self::Error> {
+ const err: &'static str = #error_str;
+ match value {
+ #(#enum_options)*
+ _ => Err(err)
+ }
+ }
+ }
+ )
+}
+
+pub fn impl_enum_abi_type(name: &syn::Ident) -> proc_macro2::TokenStream {
+ quote! {
+ impl ::evm_coder::abi::AbiType for #name {
+ const SIGNATURE: ::evm_coder::custom_signature::SignatureUnit = <u8 as ::evm_coder::abi::AbiType>::SIGNATURE;
+
+ fn is_dynamic() -> bool {
+ <u8 as ::evm_coder::abi::AbiType>::is_dynamic()
+ }
+ fn size() -> usize {
+ <u8 as ::evm_coder::abi::AbiType>::size()
+ }
+ }
+ }
+}
+
+pub fn impl_enum_abi_read(name: &syn::Ident) -> proc_macro2::TokenStream {
+ quote!(
+ impl ::evm_coder::abi::AbiRead for #name {
+ fn abi_read(reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result<Self> {
+ Ok(
+ <u8 as ::evm_coder::abi::AbiRead>::abi_read(reader)?
+ .try_into()?
+ )
+ }
+ }
+ )
+}
+
+pub fn impl_enum_abi_write(name: &syn::Ident) -> proc_macro2::TokenStream {
+ quote!(
+ impl ::evm_coder::abi::AbiWrite for #name {
+ fn abi_write(&self, writer: &mut ::evm_coder::abi::AbiWriter) {
+ ::evm_coder::abi::AbiWrite::abi_write(&(*self as u8), writer);
+ }
+ }
+ )
+}
+
+pub fn impl_enum_solidity_type_name(name: &syn::Ident) -> proc_macro2::TokenStream {
+ quote!(
+ #[cfg(feature = "stubgen")]
+ impl ::evm_coder::solidity::SolidityTypeName for #name {
+ fn solidity_name(
+ writer: &mut impl ::core::fmt::Write,
+ tc: &::evm_coder::solidity::TypeCollector,
+ ) -> ::core::fmt::Result {
+ write!(writer, "{}", tc.collect_struct::<Self>())
+ }
+
+ fn is_simple() -> bool {
+ true
+ }
+
+ fn solidity_default(
+ writer: &mut impl ::core::fmt::Write,
+ tc: &::evm_coder::solidity::TypeCollector,
+ ) -> ::core::fmt::Result {
+ write!(writer, "{}", <#name as ::evm_coder::solidity::SolidityEnum>::solidity_option(&<#name>::default()))
+ }
+ }
+ )
+}
+
+pub fn impl_enum_solidity_struct_collect<'a>(
+ name: &syn::Ident,
+ enum_options: impl Iterator<Item = &'a syn::Ident>,
+ option_count: usize,
+ enum_options_docs: impl Iterator<Item = syn::Result<Vec<proc_macro2::TokenStream>>>,
+ docs: &[proc_macro2::TokenStream],
+) -> proc_macro2::TokenStream {
+ let string_name = name.to_string();
+ let enum_options = enum_options
+ .zip(enum_options_docs)
+ .enumerate()
+ .map(|(i, (opt, doc))| {
+ let opt = proc_macro2::Literal::string(opt.to_string().as_str());
+ let doc = doc.expect("Doc parsing error");
+ let comma = if i != option_count - 1 { "," } else { "" };
+ quote! {
+ #(#doc)*
+ writeln!(str, "\t{}{}", #opt, #comma).expect("Enum format option");
+ }
+ });
+
+ quote!(
+ #[cfg(feature = "stubgen")]
+ impl ::evm_coder::solidity::StructCollect for #name {
+ fn name() -> String {
+ #string_name.into()
+ }
+
+ fn declaration() -> String {
+ use std::fmt::Write;
+
+ let mut str = String::new();
+ #(#docs)*
+ writeln!(str, "enum {} {{", <Self as ::evm_coder::solidity::StructCollect>::name()).unwrap();
+ #(#enum_options)*
+ writeln!(str, "}}").unwrap();
+ str
+ }
+ }
+ )
+}
+
+pub fn check_and_count_options(de: &syn::DataEnum) -> syn::Result<usize> {
+ let mut count = 0;
+ for v in de.variants.iter() {
+ if !v.fields.is_empty() {
+ return Err(syn::Error::new(
+ v.ident.span(),
+ "Enumeration parameters should not have fields",
+ ));
+ } else if v.discriminant.is_some() {
+ return Err(syn::Error::new(
+ v.ident.span(),
+ "Enumeration options should not have an explicit specified value",
+ ));
+ } else {
+ count += 1;
+ }
+ }
+
+ Ok(count)
+}
+
+pub fn check_repr_u8(name: &syn::Ident, attrs: &[syn::Attribute]) -> syn::Result<()> {
+ let mut has_repr = false;
+ for attr in attrs.iter() {
+ if attr.path.is_ident("repr") {
+ has_repr = true;
+ let meta = attr.parse_meta()?;
+ check_meta_u8(&meta)?;
+ }
+ }
+
+ if !has_repr {
+ return Err(syn::Error::new(name.span(), "Enum is not \"repr(u8)\""));
+ }
+
+ Ok(())
+}
+
+fn check_meta_u8(meta: &syn::Meta) -> Result<(), syn::Error> {
+ if let syn::Meta::List(p) = meta {
+ for nm in p.nested.iter() {
+ if let syn::NestedMeta::Meta(syn::Meta::Path(p)) = nm {
+ if !p.is_ident("u8") {
+ return Err(syn::Error::new(
+ p.segments
+ .first()
+ .expect("repr segments are empty")
+ .ident
+ .span(),
+ "Enum is not \"repr(u8)\"",
+ ));
+ }
+ }
+ }
+ }
+ Ok(())
+}
crates/evm-coder/procedural/src/abi_derive/derive_struct.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/evm-coder/procedural/src/abi_derive/derive_struct.rs
@@ -0,0 +1,260 @@
+use super::extract_docs;
+use quote::quote;
+
+pub fn tuple_type<'a>(
+ field_types: impl Iterator<Item = &'a syn::Type> + Clone,
+) -> proc_macro2::TokenStream {
+ let field_types = field_types.map(|ty| quote!(#ty,));
+ quote! {(#(#field_types)*)}
+}
+
+pub fn tuple_ref_type<'a>(
+ field_types: impl Iterator<Item = &'a syn::Type> + Clone,
+) -> proc_macro2::TokenStream {
+ let field_types = field_types.map(|ty| quote!(&#ty,));
+ quote! {(#(#field_types)*)}
+}
+
+pub fn tuple_data_as_ref(
+ is_named_fields: bool,
+ field_names: impl Iterator<Item = syn::Ident> + Clone,
+) -> proc_macro2::TokenStream {
+ let field_names = field_names.enumerate().map(|(i, field)| {
+ if is_named_fields {
+ quote!(&self.#field,)
+ } else {
+ let field = proc_macro2::Literal::usize_unsuffixed(i);
+ quote!(&self.#field,)
+ }
+ });
+ quote! {(#(#field_names)*)}
+}
+
+pub fn tuple_names(
+ is_named_fields: bool,
+ field_names: impl Iterator<Item = syn::Ident> + Clone,
+) -> proc_macro2::TokenStream {
+ let field_names = field_names.enumerate().map(|(i, field)| {
+ if is_named_fields {
+ quote!(#field,)
+ } else {
+ let field = proc_macro2::Ident::new(
+ format!("field{}", i).as_str(),
+ proc_macro2::Span::call_site(),
+ );
+ quote!(#field,)
+ }
+ });
+ quote! {(#(#field_names)*)}
+}
+
+pub fn struct_from_tuple(
+ name: &syn::Ident,
+ is_named_fields: bool,
+ field_names: impl Iterator<Item = syn::Ident> + Clone,
+) -> proc_macro2::TokenStream {
+ let field_names = field_names.enumerate().map(|(i, field)| {
+ if is_named_fields {
+ quote!(#field,)
+ } else {
+ let field = proc_macro2::Ident::new(
+ format!("field{}", i).as_str(),
+ proc_macro2::Span::call_site(),
+ );
+ quote!(#field,)
+ }
+ });
+
+ if is_named_fields {
+ quote! {#name {#(#field_names)*}}
+ } else {
+ quote! {#name (#(#field_names)*)}
+ }
+}
+
+pub fn map_field_to_name(field: (usize, &syn::Field)) -> syn::Ident {
+ match field.1.ident.as_ref() {
+ Some(name) => name.clone(),
+ None => {
+ let mut name = "field".to_string();
+ name.push_str(field.0.to_string().as_str());
+ syn::Ident::new(name.as_str(), proc_macro2::Span::call_site())
+ }
+ }
+}
+
+pub fn map_field_to_type(field: &syn::Field) -> &syn::Type {
+ &field.ty
+}
+
+pub fn map_field_to_doc(field: &syn::Field) -> syn::Result<Vec<proc_macro2::TokenStream>> {
+ extract_docs(&field.attrs, true)
+}
+
+pub fn impl_can_be_placed_in_vec(ident: &syn::Ident) -> proc_macro2::TokenStream {
+ quote! {
+ impl ::evm_coder::sealed::CanBePlacedInVec for #ident {}
+ }
+}
+
+pub fn impl_struct_abi_type(
+ name: &syn::Ident,
+ tuple_type: proc_macro2::TokenStream,
+) -> proc_macro2::TokenStream {
+ quote! {
+ impl ::evm_coder::abi::AbiType for #name {
+ const SIGNATURE: ::evm_coder::custom_signature::SignatureUnit = <#tuple_type as ::evm_coder::abi::AbiType>::SIGNATURE;
+ fn is_dynamic() -> bool {
+ <#tuple_type as ::evm_coder::abi::AbiType>::is_dynamic()
+ }
+ fn size() -> usize {
+ <#tuple_type as ::evm_coder::abi::AbiType>::size()
+ }
+ }
+ }
+}
+
+pub fn impl_struct_abi_read(
+ name: &syn::Ident,
+ tuple_type: proc_macro2::TokenStream,
+ tuple_names: proc_macro2::TokenStream,
+ struct_from_tuple: proc_macro2::TokenStream,
+) -> proc_macro2::TokenStream {
+ quote!(
+ impl ::evm_coder::abi::AbiRead for #name {
+ fn abi_read(reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result<Self> {
+ let #tuple_names = <#tuple_type as ::evm_coder::abi::AbiRead>::abi_read(reader)?;
+ Ok(#struct_from_tuple)
+ }
+ }
+ )
+}
+
+pub fn impl_struct_abi_write(
+ name: &syn::Ident,
+ _is_named_fields: bool,
+ tuple_type: proc_macro2::TokenStream,
+ tuple_data: proc_macro2::TokenStream,
+) -> proc_macro2::TokenStream {
+ quote!(
+ impl ::evm_coder::abi::AbiWrite for #name {
+ fn abi_write(&self, writer: &mut ::evm_coder::abi::AbiWriter) {
+ <#tuple_type as ::evm_coder::abi::AbiWrite>::abi_write(&#tuple_data, writer)
+ }
+ }
+ )
+}
+
+pub fn impl_struct_solidity_type<'a>(
+ name: &syn::Ident,
+ field_types: impl Iterator<Item = &'a syn::Type> + Clone,
+ params_count: usize,
+) -> proc_macro2::TokenStream {
+ let len = proc_macro2::Literal::usize_suffixed(params_count);
+ quote! {
+ #[cfg(feature = "stubgen")]
+ impl ::evm_coder::solidity::SolidityType for #name {
+ fn names(tc: &::evm_coder::solidity::TypeCollector) -> Vec<String> {
+ let mut collected =
+ Vec::with_capacity(<Self as ::evm_coder::solidity::SolidityType>::len());
+ #({
+ let mut out = String::new();
+ <#field_types as ::evm_coder::solidity::SolidityTypeName>::solidity_name(&mut out, tc)
+ .expect("no fmt error");
+ collected.push(out);
+ })*
+ collected
+ }
+
+ fn len() -> usize {
+ #len
+ }
+ }
+ }
+}
+
+pub fn impl_struct_solidity_type_name<'a>(
+ name: &syn::Ident,
+ field_types: impl Iterator<Item = &'a syn::Type> + Clone,
+ params_count: usize,
+) -> proc_macro2::TokenStream {
+ let arg_dafaults = field_types.enumerate().map(|(i, ty)| {
+ let mut defult_value = quote!(<#ty as ::evm_coder::solidity::SolidityTypeName
+ >::solidity_default(writer, tc)?;);
+ let last_item = params_count - 1;
+ if i != last_item {
+ defult_value.extend(quote! {write!(writer, ",")?;})
+ }
+ defult_value
+ });
+
+ quote! {
+ #[cfg(feature = "stubgen")]
+ impl ::evm_coder::solidity::SolidityTypeName for #name {
+ fn solidity_name(
+ writer: &mut impl ::core::fmt::Write,
+ tc: &::evm_coder::solidity::TypeCollector,
+ ) -> ::core::fmt::Result {
+ write!(writer, "{}", tc.collect_struct::<Self>())
+ }
+
+ fn is_simple() -> bool {
+ false
+ }
+
+ fn solidity_default(
+ writer: &mut impl ::core::fmt::Write,
+ tc: &::evm_coder::solidity::TypeCollector,
+ ) -> ::core::fmt::Result {
+ write!(writer, "{}(", tc.collect_struct::<Self>())?;
+
+ #(#arg_dafaults)*
+
+ write!(writer, ")")
+ }
+ }
+ }
+}
+
+pub fn impl_struct_solidity_struct_collect<'a>(
+ name: &syn::Ident,
+ field_names: impl Iterator<Item = proc_macro2::Ident> + Clone,
+ field_types: impl Iterator<Item = &'a syn::Type> + Clone,
+ field_docs: impl Iterator<Item = syn::Result<Vec<proc_macro2::TokenStream>>> + Clone,
+ docs: &[proc_macro2::TokenStream],
+) -> syn::Result<proc_macro2::TokenStream> {
+ let string_name = name.to_string();
+ let name_type = field_names
+ .into_iter()
+ .zip(field_types)
+ .zip(field_docs)
+ .map(|((name, ty), doc)| {
+ let field_docs = doc.expect("Doc parse error");
+ let name = format!("{}", name);
+ quote!(
+ #(#field_docs)*
+ write!(str, "\t{} ", <#ty as ::evm_coder::solidity::StructCollect>::name()).unwrap();
+ writeln!(str, "{};", #name).unwrap();
+ )
+ });
+
+ Ok(quote! {
+ #[cfg(feature = "stubgen")]
+ impl ::evm_coder::solidity::StructCollect for #name {
+ fn name() -> String {
+ #string_name.into()
+ }
+
+ fn declaration() -> String {
+ use std::fmt::Write;
+
+ let mut str = String::new();
+ #(#docs)*
+ writeln!(str, "struct {} {{", Self::name()).unwrap();
+ #(#name_type)*
+ writeln!(str, "}}").unwrap();
+ str
+ }
+ }
+ })
+}
crates/evm-coder/procedural/src/abi_derive/mod.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/evm-coder/procedural/src/abi_derive/mod.rs
@@ -0,0 +1,145 @@
+mod derive_enum;
+mod derive_struct;
+
+use quote::quote;
+use derive_struct::*;
+use derive_enum::*;
+
+pub(crate) fn impl_abi_macro(ast: &syn::DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
+ let name = &ast.ident;
+ match &ast.data {
+ syn::Data::Struct(ds) => expand_struct(ds, ast),
+ syn::Data::Enum(de) => expand_enum(de, ast),
+ syn::Data::Union(_) => Err(syn::Error::new(name.span(), "Unions not supported")),
+ }
+}
+
+fn expand_struct(
+ ds: &syn::DataStruct,
+ ast: &syn::DeriveInput,
+) -> syn::Result<proc_macro2::TokenStream> {
+ let name = &ast.ident;
+ let docs = extract_docs(&ast.attrs, false)?;
+ let (is_named_fields, field_names, field_types, field_docs, params_count) = match ds.fields {
+ syn::Fields::Named(ref fields) => Ok((
+ true,
+ fields.named.iter().enumerate().map(map_field_to_name),
+ fields.named.iter().map(map_field_to_type),
+ fields.named.iter().map(map_field_to_doc),
+ fields.named.len(),
+ )),
+ syn::Fields::Unnamed(ref fields) => Ok((
+ false,
+ fields.unnamed.iter().enumerate().map(map_field_to_name),
+ fields.unnamed.iter().map(map_field_to_type),
+ fields.unnamed.iter().map(map_field_to_doc),
+ fields.unnamed.len(),
+ )),
+ syn::Fields::Unit => Err(syn::Error::new(name.span(), "Unit structs not supported")),
+ }?;
+
+ if params_count == 0 {
+ return Err(syn::Error::new(name.span(), "Empty structs not supported"));
+ };
+
+ let tuple_type = tuple_type(field_types.clone());
+ let tuple_ref_type = tuple_ref_type(field_types.clone());
+ let tuple_data = tuple_data_as_ref(is_named_fields, field_names.clone());
+ let tuple_names = tuple_names(is_named_fields, field_names.clone());
+ let struct_from_tuple = struct_from_tuple(name, is_named_fields, field_names.clone());
+
+ let can_be_plcaed_in_vec = impl_can_be_placed_in_vec(name);
+ let abi_type = impl_struct_abi_type(name, tuple_type.clone());
+ let abi_read = impl_struct_abi_read(name, tuple_type, tuple_names, struct_from_tuple);
+ let abi_write = impl_struct_abi_write(name, is_named_fields, tuple_ref_type, tuple_data);
+ let solidity_type = impl_struct_solidity_type(name, field_types.clone(), params_count);
+ let solidity_type_name =
+ impl_struct_solidity_type_name(name, field_types.clone(), params_count);
+ let solidity_struct_collect =
+ impl_struct_solidity_struct_collect(name, field_names, field_types, field_docs, &docs)?;
+
+ Ok(quote! {
+ #can_be_plcaed_in_vec
+ #abi_type
+ #abi_read
+ #abi_write
+ #solidity_type
+ #solidity_type_name
+ #solidity_struct_collect
+ })
+}
+
+fn expand_enum(
+ de: &syn::DataEnum,
+ ast: &syn::DeriveInput,
+) -> syn::Result<proc_macro2::TokenStream> {
+ let name = &ast.ident;
+ check_repr_u8(name, &ast.attrs)?;
+ let docs = extract_docs(&ast.attrs, false)?;
+ let option_count = check_and_count_options(de)?;
+ let enum_options = de.variants.iter().map(|v| &v.ident);
+ let enum_options_docs = de.variants.iter().map(|v| extract_docs(&v.attrs, true));
+
+ let from = impl_enum_from_u8(name, enum_options.clone());
+ let solidity_option = impl_solidity_option(name, enum_options.clone());
+ let can_be_plcaed_in_vec = impl_can_be_placed_in_vec(name);
+ let abi_type = impl_enum_abi_type(name);
+ let abi_read = impl_enum_abi_read(name);
+ let abi_write = impl_enum_abi_write(name);
+ let solidity_type_name = impl_enum_solidity_type_name(name);
+ let solidity_struct_collect = impl_enum_solidity_struct_collect(
+ name,
+ enum_options,
+ option_count,
+ enum_options_docs,
+ &docs,
+ );
+
+ Ok(quote! {
+ #from
+ #solidity_option
+ #can_be_plcaed_in_vec
+ #abi_type
+ #abi_read
+ #abi_write
+ #solidity_type_name
+ #solidity_struct_collect
+ })
+}
+
+fn extract_docs(
+ attrs: &[syn::Attribute],
+ is_field_doc: bool,
+) -> syn::Result<Vec<proc_macro2::TokenStream>> {
+ attrs
+ .iter()
+ .filter_map(|attr| {
+ if let Some(ps) = attr.path.segments.first() {
+ if ps.ident == "doc" {
+ let meta = match attr.parse_meta() {
+ Ok(meta) => meta,
+ Err(e) => return Some(Err(e)),
+ };
+ match meta {
+ syn::Meta::NameValue(mnv) => match &mnv.lit {
+ syn::Lit::Str(ls) => return Some(Ok(ls.value())),
+ _ => unreachable!(),
+ },
+ _ => unreachable!(),
+ }
+ }
+ }
+ None
+ })
+ .enumerate()
+ .map(|(i, doc)| {
+ let doc = doc?;
+ let doc = doc.trim();
+ let dev = if i == 0 { " @dev" } else { "" };
+ let tab = if is_field_doc { "\t" } else { "" };
+ Ok(quote! {
+ writeln!(str, "{}///{} {}", #tab, #dev, #doc).unwrap();
+ })
+ })
+ .collect()
+}
crates/evm-coder/procedural/src/lib.rsdiffbeforeafterboth--- a/crates/evm-coder/procedural/src/lib.rs
+++ b/crates/evm-coder/procedural/src/lib.rs
@@ -25,6 +25,7 @@
parse_macro_input, spanned::Spanned,
};
+mod abi_derive;
mod solidity_interface;
mod to_log;
@@ -242,3 +243,13 @@
}
.into()
}
+
+#[proc_macro_derive(AbiCoder)]
+pub fn abi_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+ let ast = syn::parse(input).unwrap();
+ let ts = match abi_derive::impl_abi_macro(&ast) {
+ Ok(e) => e,
+ Err(e) => e.to_compile_error(),
+ };
+ ts.into()
+}
crates/evm-coder/procedural/src/solidity_interface.rsdiffbeforeafterboth--- a/crates/evm-coder/procedural/src/solidity_interface.rs
+++ b/crates/evm-coder/procedural/src/solidity_interface.rs
@@ -404,7 +404,11 @@
let name = &self.name;
let ty = &self.ty;
quote! {
- #name: <#ty>::abi_read(reader)?
+ #name: {
+ let value = <#ty as ::evm_coder::abi::AbiRead>::abi_read(reader)?;
+ if !is_dynamic {reader.bytes_read(<#ty as ::evm_coder::abi::AbiType>::size())};
+ value
+ }
}
}
@@ -630,17 +634,18 @@
let pascal_name = &self.pascal_name;
let screaming_name = &self.screaming_name;
if self.has_normal_args {
- let parsers = self
- .args
- .iter()
- .filter(|a| !a.is_special())
- .map(|a| a.expand_parse());
+ let args_iter = self.args.iter().filter(|a| !a.is_special());
+ let arg_type = args_iter.clone().map(|a| &a.ty);
+ let parsers = args_iter.map(|a| a.expand_parse());
quote! {
- Self::#screaming_name => return Ok(Some(Self::#pascal_name {
- #(
- #parsers,
- )*
- }))
+ Self::#screaming_name => {
+ let is_dynamic = false #(|| <#arg_type as ::evm_coder::abi::AbiType>::is_dynamic())*;
+ return Ok(Some(Self::#pascal_name {
+ #(
+ #parsers,
+ )*
+ }))
+ }
}
} else {
quote! { Self::#screaming_name => return Ok(Some(Self::#pascal_name)) }
crates/evm-coder/src/abi/impls.rsdiffbeforeafterboth--- a/crates/evm-coder/src/abi/impls.rs
+++ b/crates/evm-coder/src/abi/impls.rs
@@ -1,8 +1,8 @@
use crate::{
+ custom_signature::SignatureUnit,
execution::{Result, ResultWithPostInfo, WithPostDispatchInfo},
+ make_signature, sealed,
types::*,
- make_signature,
- custom_signature::SignatureUnit,
};
use super::{traits::*, ABI_ALIGNMENT, AbiReader, AbiWriter};
use primitive_types::{U256, H160};
@@ -10,12 +10,12 @@
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
-macro_rules! impl_abi_readable {
- ($ty:ty, $method:ident, $dynamic:literal) => {
+macro_rules! impl_abi_type {
+ ($ty:ty, $name:ident, $dynamic:literal) => {
impl sealed::CanBePlacedInVec for $ty {}
impl AbiType for $ty {
- const SIGNATURE: SignatureUnit = make_signature!(new fixed(stringify!($ty)));
+ const SIGNATURE: SignatureUnit = make_signature!(new fixed(stringify!($name)));
fn is_dynamic() -> bool {
$dynamic
@@ -25,7 +25,11 @@
ABI_ALIGNMENT
}
}
+ };
+}
+macro_rules! impl_abi_readable {
+ ($ty:ty, $method:ident) => {
impl AbiRead for $ty {
fn abi_read(reader: &mut AbiReader) -> Result<$ty> {
reader.$method()
@@ -34,79 +38,93 @@
};
}
-impl_abi_readable!(uint32, uint32, false);
-impl_abi_readable!(uint64, uint64, false);
-impl_abi_readable!(uint128, uint128, false);
-impl_abi_readable!(uint256, uint256, false);
-impl_abi_readable!(bytes4, bytes4, false);
-impl_abi_readable!(address, address, false);
-impl_abi_readable!(string, string, true);
+macro_rules! impl_abi_writeable {
+ ($ty:ty, $method:ident) => {
+ impl AbiWrite for $ty {
+ fn abi_write(&self, writer: &mut AbiWriter) {
+ writer.$method(&self)
+ }
+ }
+ };
+}
-impl sealed::CanBePlacedInVec for bool {}
+macro_rules! impl_abi {
+ ($ty:ty, $method:ident, $dynamic:literal) => {
+ impl_abi_type!($ty, $method, $dynamic);
+ impl_abi_readable!($ty, $method);
+ impl_abi_writeable!($ty, $method);
+ };
+}
-impl AbiType for bool {
- const SIGNATURE: SignatureUnit = make_signature!(new fixed("bool"));
+impl_abi!(bool, bool, false);
+impl_abi!(u8, uint8, false);
+impl_abi!(u32, uint32, false);
+impl_abi!(u64, uint64, false);
+impl_abi!(u128, uint128, false);
+impl_abi!(U256, uint256, false);
+impl_abi!(H160, address, false);
+impl_abi!(string, string, true);
- fn is_dynamic() -> bool {
- false
- }
- fn size() -> usize {
- ABI_ALIGNMENT
+impl_abi_writeable!(&str, string);
+
+impl_abi_type!(bytes, bytes, true);
+
+impl AbiRead for bytes {
+ fn abi_read(reader: &mut AbiReader) -> Result<bytes> {
+ Ok(bytes(reader.bytes()?))
}
}
-impl AbiRead for bool {
- fn abi_read(reader: &mut AbiReader) -> Result<bool> {
- reader.bool()
+
+impl AbiWrite for bytes {
+ fn abi_write(&self, writer: &mut AbiWriter) {
+ writer.bytes(self.0.as_slice())
}
}
-
-impl AbiType for uint8 {
- const SIGNATURE: SignatureUnit = make_signature!(new fixed("uint8"));
- fn is_dynamic() -> bool {
- false
- }
- fn size() -> usize {
- ABI_ALIGNMENT
+impl_abi_type!(bytes4, bytes4, false);
+impl AbiRead for bytes4 {
+ fn abi_read(reader: &mut AbiReader) -> Result<bytes4> {
+ reader.bytes4()
}
}
-impl AbiRead for uint8 {
- fn abi_read(reader: &mut AbiReader) -> Result<uint8> {
- reader.uint8()
+
+impl<T: AbiWrite> AbiWrite for &T {
+ fn abi_write(&self, writer: &mut AbiWriter) {
+ T::abi_write(self, writer);
}
}
-impl AbiType for bytes {
- const SIGNATURE: SignatureUnit = make_signature!(new fixed("bytes"));
+impl<T: AbiType> AbiType for &T {
+ const SIGNATURE: SignatureUnit = T::SIGNATURE;
fn is_dynamic() -> bool {
- true
+ T::is_dynamic()
}
+
fn size() -> usize {
- ABI_ALIGNMENT
- }
-}
-impl AbiRead for bytes {
- fn abi_read(reader: &mut AbiReader) -> Result<bytes> {
- Ok(bytes(reader.bytes()?))
+ T::size()
}
}
-impl<R: AbiRead + sealed::CanBePlacedInVec> AbiRead for Vec<R> {
- fn abi_read(reader: &mut AbiReader) -> Result<Vec<R>> {
+impl<T: AbiType + AbiRead + sealed::CanBePlacedInVec> AbiRead for Vec<T> {
+ fn abi_read(reader: &mut AbiReader) -> Result<Vec<T>> {
let mut sub = reader.subresult(None)?;
let size = sub.uint32()? as usize;
sub.subresult_offset = sub.offset;
+ let is_dynamic = <T as AbiType>::is_dynamic();
let mut out = Vec::with_capacity(size);
for _ in 0..size {
- out.push(<R>::abi_read(&mut sub)?);
+ out.push(<T as AbiRead>::abi_read(&mut sub)?);
+ if !is_dynamic {
+ sub.bytes_read(<T as AbiType>::size());
+ };
}
Ok(out)
}
}
-impl<R: AbiType> AbiType for Vec<R> {
- const SIGNATURE: SignatureUnit = make_signature!(new nameof(R::SIGNATURE) fixed("[]"));
+impl<T: AbiType> AbiType for Vec<T> {
+ const SIGNATURE: SignatureUnit = make_signature!(new nameof(T::SIGNATURE) fixed("[]"));
fn is_dynamic() -> bool {
true
@@ -114,42 +132,6 @@
fn size() -> usize {
ABI_ALIGNMENT
- }
-}
-
-impl sealed::CanBePlacedInVec for EthCrossAccount {}
-
-impl AbiType for EthCrossAccount {
- const SIGNATURE: SignatureUnit = make_signature!(new fixed("(address,uint256)"));
-
- fn is_dynamic() -> bool {
- address::is_dynamic() || uint256::is_dynamic()
- }
-
- fn size() -> usize {
- <address as AbiType>::size() + <uint256 as AbiType>::size()
- }
-}
-
-impl AbiRead for EthCrossAccount {
- fn abi_read(reader: &mut AbiReader) -> Result<EthCrossAccount> {
- let size = if !EthCrossAccount::is_dynamic() {
- Some(<EthCrossAccount as AbiType>::size())
- } else {
- None
- };
- let mut subresult = reader.subresult(size)?;
- let eth = <address>::abi_read(&mut subresult)?;
- let sub = <uint256>::abi_read(&mut subresult)?;
-
- Ok(EthCrossAccount { eth, sub })
- }
-}
-
-impl AbiWrite for EthCrossAccount {
- fn abi_write(&self, writer: &mut AbiWriter) {
- self.eth.abi_write(writer);
- self.sub.abi_write(writer);
}
}
@@ -188,36 +170,6 @@
}
}
-macro_rules! impl_abi_writeable {
- ($ty:ty, $method:ident) => {
- impl AbiWrite for $ty {
- fn abi_write(&self, writer: &mut AbiWriter) {
- writer.$method(&self)
- }
- }
- };
-}
-
-impl_abi_writeable!(u8, uint8);
-impl_abi_writeable!(u32, uint32);
-impl_abi_writeable!(u128, uint128);
-impl_abi_writeable!(U256, uint256);
-impl_abi_writeable!(H160, address);
-impl_abi_writeable!(bool, bool);
-impl_abi_writeable!(&str, string);
-
-impl AbiWrite for string {
- fn abi_write(&self, writer: &mut AbiWriter) {
- writer.string(self)
- }
-}
-
-impl AbiWrite for bytes {
- fn abi_write(&self, writer: &mut AbiWriter) {
- writer.bytes(self.0.as_slice())
- }
-}
-
impl<T: AbiWrite + AbiType> AbiWrite for Vec<T> {
fn abi_write(&self, writer: &mut AbiWriter) {
let is_dynamic = T::is_dynamic();
@@ -295,14 +247,19 @@
impl<$($ident),+> AbiRead for ($($ident,)+)
where
- $($ident: AbiRead,)+
- ($($ident,)+): AbiType,
+ Self: AbiType,
+ $($ident: AbiRead + AbiType,)+
{
fn abi_read(reader: &mut AbiReader) -> Result<($($ident,)+)> {
- let size = if !<($($ident,)+)>::is_dynamic() { Some(<($($ident,)+)>::size()) } else { None };
+ let is_dynamic = <Self>::is_dynamic();
+ let size = if !is_dynamic { Some(<Self>::size()) } else { None };
let mut subresult = reader.subresult(size)?;
Ok((
- $(<$ident>::abi_read(&mut subresult)?,)+
+ $({
+ let value = <$ident>::abi_read(&mut subresult)?;
+ if !is_dynamic {subresult.bytes_read(<$ident as AbiType>::size())};
+ value
+ },)+
))
}
}
@@ -310,11 +267,11 @@
#[allow(non_snake_case)]
impl<$($ident),+> AbiWrite for ($($ident,)+)
where
- $($ident: AbiWrite,)+
+ $($ident: AbiWrite + AbiType,)+
{
fn abi_write(&self, writer: &mut AbiWriter) {
let ($($ident,)+) = self;
- if writer.is_dynamic {
+ if <Self as AbiType>::is_dynamic() {
let mut sub = AbiWriter::new();
$($ident.abi_write(&mut sub);)+
writer.write_subresult(sub);
crates/evm-coder/src/abi/mod.rsdiffbeforeafterboth--- a/crates/evm-coder/src/abi/mod.rs
+++ b/crates/evm-coder/src/abi/mod.rs
@@ -75,19 +75,19 @@
buf: &[u8],
offset: usize,
pad_start: usize,
- pad_size: usize,
+ pad_end: usize,
block_start: usize,
- block_size: usize,
+ block_end: usize,
) -> Result<[u8; S]> {
if buf.len() - offset < ABI_ALIGNMENT {
return Err(Error::Error(ExitError::OutOfOffset));
}
let mut block = [0; S];
- let is_pad_zeroed = buf[pad_start..pad_size].iter().all(|&v| v == 0);
+ let is_pad_zeroed = buf[pad_start..pad_end].iter().all(|&v| v == 0);
if !is_pad_zeroed {
return Err(Error::Error(ExitError::InvalidRange));
}
- block.copy_from_slice(&buf[block_start..block_size]);
+ block.copy_from_slice(&buf[block_start..block_end]);
Ok(block)
}
@@ -186,11 +186,10 @@
/// Slice recursive buffer, advance one word for buffer offset
/// If `size` is [`None`] then [`Self::offset`] and [`Self::subresult_offset`] evals from [`Self::buf`].
- fn subresult(&mut self, size: Option<usize>) -> Result<AbiReader<'i>> {
+ pub fn subresult(&mut self, size: Option<usize>) -> Result<AbiReader<'i>> {
let subresult_offset = self.subresult_offset;
let offset = if let Some(size) = size {
self.offset += size;
- self.subresult_offset += size;
0
} else {
self.uint32()? as usize
@@ -208,6 +207,11 @@
})
}
+ /// Notify about readed data portion.
+ pub fn bytes_read(&mut self, size: usize) {
+ self.subresult_offset += size;
+ }
+
/// Is this parser reached end of buffer?
pub fn is_finished(&self) -> bool {
self.buf.len() == self.offset
@@ -277,6 +281,11 @@
self.write_padleft(&u32::to_be_bytes(*value))
}
+ /// Write [`u64`] to end of buffer
+ pub fn uint64(&mut self, value: &u64) {
+ self.write_padleft(&u64::to_be_bytes(*value))
+ }
+
/// Write [`u128`] to end of buffer
pub fn uint128(&mut self, value: &u128) {
self.write_padleft(&u128::to_be_bytes(*value))
crates/evm-coder/src/abi/test.rsdiffbeforeafterboth--- a/crates/evm-coder/src/abi/test.rs
+++ b/crates/evm-coder/src/abi/test.rs
@@ -6,36 +6,25 @@
use super::{AbiReader, AbiWriter};
use hex_literal::hex;
use primitive_types::{H160, U256};
-use concat_idents::concat_idents;
-macro_rules! test_impl {
- ($name:ident, $type:ty, $function_identifier:expr, $decoded_data:expr, $encoded_data:expr) => {
- concat_idents!(test_name = encode_decode_, $name {
- #[test]
- fn test_name() {
- let function_identifier: u32 = $function_identifier;
- let decoded_data = $decoded_data;
- let encoded_data = $encoded_data;
-
- let (call, mut decoder) = AbiReader::new_call(encoded_data).unwrap();
- assert_eq!(call, u32::to_be_bytes(function_identifier));
- let data = <$type>::abi_read(&mut decoder).unwrap();
- assert_eq!(data, decoded_data);
+fn test_impl<T>(function_identifier: u32, decoded_data: T, encoded_data: &[u8])
+where
+ T: AbiWrite + AbiRead + std::cmp::PartialEq + std::fmt::Debug,
+{
+ let (call, mut decoder) = AbiReader::new_call(encoded_data).unwrap();
+ assert_eq!(call, u32::to_be_bytes(function_identifier));
+ let data = <T>::abi_read(&mut decoder).unwrap();
+ assert_eq!(data, decoded_data);
- let mut writer = AbiWriter::new_call(function_identifier);
- decoded_data.abi_write(&mut writer);
- let ed = writer.finish();
- similar_asserts::assert_eq!(encoded_data, ed.as_slice());
- }
- });
- };
+ let mut writer = AbiWriter::new_call(function_identifier);
+ decoded_data.abi_write(&mut writer);
+ let ed = writer.finish();
+ similar_asserts::assert_eq!(encoded_data, ed.as_slice());
}
macro_rules! test_impl_uint {
($type:ident) => {
- test_impl!(
- $type,
- $type,
+ test_impl::<$type>(
0xdeadbeef,
255 as $type,
&hex!(
@@ -43,101 +32,148 @@
deadbeef
00000000000000000000000000000000000000000000000000000000000000ff
"
- )
+ ),
);
};
}
-test_impl_uint!(uint8);
-test_impl_uint!(uint32);
-test_impl_uint!(uint128);
+#[test]
+fn encode_decode_uint8() {
+ test_impl_uint!(uint8);
+}
-test_impl!(
- uint256,
- uint256,
- 0xdeadbeef,
- U256([255, 0, 0, 0]),
- &hex!(
- "
- deadbeef
- 00000000000000000000000000000000000000000000000000000000000000ff
- "
- )
-);
+#[test]
+fn encode_decode_uint32() {
+ test_impl_uint!(uint32);
+}
-test_impl!(
- vec_tuple_address_uint256,
- Vec<(address, uint256)>,
- 0x1ACF2D55,
- vec![
- (
- H160(hex!("2D2FF76104B7BACB2E8F6731D5BFC184EBECDDBC")),
- U256([10, 0, 0, 0]),
- ),
- (
- H160(hex!("AB8E3D9134955566483B11E6825C9223B6737B10")),
- U256([20, 0, 0, 0]),
- ),
- (
- H160(hex!("8C582BDF2953046705FC56F189385255EFC1BE18")),
- U256([30, 0, 0, 0]),
- ),
- ],
- &hex!(
- "
- 1ACF2D55
- 0000000000000000000000000000000000000000000000000000000000000020 // offset of (address, uint256)[]
- 0000000000000000000000000000000000000000000000000000000000000003 // length of (address, uint256)[]
+#[test]
+fn encode_decode_uint128() {
+ test_impl_uint!(uint128);
+}
- 0000000000000000000000002D2FF76104B7BACB2E8F6731D5BFC184EBECDDBC // address
- 000000000000000000000000000000000000000000000000000000000000000A // uint256
+#[test]
+fn encode_decode_uint256() {
+ test_impl::<uint256>(
+ 0xdeadbeef,
+ U256([255, 0, 0, 0]),
+ &hex!(
+ "
+ deadbeef
+ 00000000000000000000000000000000000000000000000000000000000000ff
+ "
+ ),
+ );
+}
- 000000000000000000000000AB8E3D9134955566483B11E6825C9223B6737B10 // address
- 0000000000000000000000000000000000000000000000000000000000000014 // uint256
+#[test]
+fn encode_decode_string() {
+ test_impl::<String>(
+ 0xdeadbeef,
+ "some string".to_string(),
+ &hex!(
+ "
+ deadbeef
+ 0000000000000000000000000000000000000000000000000000000000000020
+ 000000000000000000000000000000000000000000000000000000000000000b
+ 736f6d6520737472696e67000000000000000000000000000000000000000000
+ "
+ ),
+ );
+}
- 0000000000000000000000008C582BDF2953046705FC56F189385255EFC1BE18 // address
- 000000000000000000000000000000000000000000000000000000000000001E // uint256
- "
- )
-);
+#[test]
+fn encode_decode_tuple_string() {
+ test_impl::<(String,)>(
+ 0xdeadbeef,
+ ("some string".to_string(),),
+ &hex!(
+ "
+ deadbeef
+ 0000000000000000000000000000000000000000000000000000000000000020
+ 0000000000000000000000000000000000000000000000000000000000000020
+ 000000000000000000000000000000000000000000000000000000000000000b
+ 736f6d6520737472696e67000000000000000000000000000000000000000000
+ "
+ ),
+ );
+}
-test_impl!(
- vec_tuple_uint256_string,
- Vec<(uint256, string)>,
- 0xdeadbeef,
- vec![
- (1.into(), "Test URI 0".to_string()),
- (11.into(), "Test URI 1".to_string()),
- (12.into(), "Test URI 2".to_string()),
- ],
- &hex!(
- "
- deadbeef
- 0000000000000000000000000000000000000000000000000000000000000020 // offset of (uint256, string)[]
- 0000000000000000000000000000000000000000000000000000000000000003 // length of (uint256, string)[]
+#[test]
+fn encode_decode_vec_tuple_address_uint256() {
+ test_impl::<Vec<(address, uint256)>>(
+ 0x1ACF2D55,
+ vec![
+ (
+ H160(hex!("2D2FF76104B7BACB2E8F6731D5BFC184EBECDDBC")),
+ U256([10, 0, 0, 0]),
+ ),
+ (
+ H160(hex!("AB8E3D9134955566483B11E6825C9223B6737B10")),
+ U256([20, 0, 0, 0]),
+ ),
+ (
+ H160(hex!("8C582BDF2953046705FC56F189385255EFC1BE18")),
+ U256([30, 0, 0, 0]),
+ ),
+ ],
+ &hex!(
+ "
+ 1ACF2D55
+ 0000000000000000000000000000000000000000000000000000000000000020 // offset of (address, uint256)[]
+ 0000000000000000000000000000000000000000000000000000000000000003 // length of (address, uint256)[]
- 0000000000000000000000000000000000000000000000000000000000000060 // offset of first elem
- 00000000000000000000000000000000000000000000000000000000000000e0 // offset of second elem
- 0000000000000000000000000000000000000000000000000000000000000160 // offset of third elem
+ 0000000000000000000000002D2FF76104B7BACB2E8F6731D5BFC184EBECDDBC // address
+ 000000000000000000000000000000000000000000000000000000000000000A // uint256
- 0000000000000000000000000000000000000000000000000000000000000001 // first token id? #60
- 0000000000000000000000000000000000000000000000000000000000000040 // offset of string
- 000000000000000000000000000000000000000000000000000000000000000a // size of string
- 5465737420555249203000000000000000000000000000000000000000000000 // string
+ 000000000000000000000000AB8E3D9134955566483B11E6825C9223B6737B10 // address
+ 0000000000000000000000000000000000000000000000000000000000000014 // uint256
- 000000000000000000000000000000000000000000000000000000000000000b // second token id? Why ==11? #e0
- 0000000000000000000000000000000000000000000000000000000000000040 // offset of string
- 000000000000000000000000000000000000000000000000000000000000000a // size of string
- 5465737420555249203100000000000000000000000000000000000000000000 // string
+ 0000000000000000000000008C582BDF2953046705FC56F189385255EFC1BE18 // address
+ 000000000000000000000000000000000000000000000000000000000000001E // uint256
+ "
+ )
+ );
+}
- 000000000000000000000000000000000000000000000000000000000000000c // third token id? Why ==12? #160
- 0000000000000000000000000000000000000000000000000000000000000040 // offset of string
- 000000000000000000000000000000000000000000000000000000000000000a // size of string
- 5465737420555249203200000000000000000000000000000000000000000000 // string
- "
- )
-);
+#[test]
+fn encode_decode_vec_tuple_uint256_string() {
+ test_impl::<Vec<(uint256, string)>>(
+ 0xdeadbeef,
+ vec![
+ (1.into(), "Test URI 0".to_string()),
+ (11.into(), "Test URI 1".to_string()),
+ (12.into(), "Test URI 2".to_string()),
+ ],
+ &hex!(
+ "
+ deadbeef
+ 0000000000000000000000000000000000000000000000000000000000000020 // offset of (uint256, string)[]
+ 0000000000000000000000000000000000000000000000000000000000000003 // length of (uint256, string)[]
+ 0000000000000000000000000000000000000000000000000000000000000060 // offset of first elem
+ 00000000000000000000000000000000000000000000000000000000000000e0 // offset of second elem
+ 0000000000000000000000000000000000000000000000000000000000000160 // offset of third elem
+
+ 0000000000000000000000000000000000000000000000000000000000000001 // first token id? #60
+ 0000000000000000000000000000000000000000000000000000000000000040 // offset of string
+ 000000000000000000000000000000000000000000000000000000000000000a // size of string
+ 5465737420555249203000000000000000000000000000000000000000000000 // string
+
+ 000000000000000000000000000000000000000000000000000000000000000b // second token id? Why ==11? #e0
+ 0000000000000000000000000000000000000000000000000000000000000040 // offset of string
+ 000000000000000000000000000000000000000000000000000000000000000a // size of string
+ 5465737420555249203100000000000000000000000000000000000000000000 // string
+
+ 000000000000000000000000000000000000000000000000000000000000000c // third token id? Why ==12? #160
+ 0000000000000000000000000000000000000000000000000000000000000040 // offset of string
+ 000000000000000000000000000000000000000000000000000000000000000a // size of string
+ 5465737420555249203200000000000000000000000000000000000000000000 // string
+ "
+ )
+ );
+}
+
#[test]
fn dynamic_after_static() {
let mut encoder = AbiWriter::new();
@@ -235,63 +271,270 @@
similar_asserts::assert_eq!(encoded_data, ed.as_slice());
}
-test_impl!(
- vec_tuple_string_bytes,
- Vec<(string, bytes)>,
- 0xdeadbeef,
- vec![
- (
- "Test URI 0".to_string(),
- bytes(vec![
- 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
- 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
- 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
- 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
- ])
+#[test]
+fn encode_decode_vec_tuple_string_bytes() {
+ test_impl::<Vec<(string, bytes)>>(
+ 0xdeadbeef,
+ vec![
+ (
+ "Test URI 0".to_string(),
+ bytes(vec![
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ ]),
+ ),
+ (
+ "Test URI 1".to_string(),
+ bytes(vec![
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ ]),
+ ),
+ ("Test URI 2".to_string(), bytes(vec![0x33, 0x33])),
+ ],
+ &hex!(
+ "
+ deadbeef
+ 0000000000000000000000000000000000000000000000000000000000000020
+ 0000000000000000000000000000000000000000000000000000000000000003
+
+ 0000000000000000000000000000000000000000000000000000000000000060
+ 0000000000000000000000000000000000000000000000000000000000000140
+ 0000000000000000000000000000000000000000000000000000000000000220
+
+ 0000000000000000000000000000000000000000000000000000000000000040
+ 0000000000000000000000000000000000000000000000000000000000000080
+ 000000000000000000000000000000000000000000000000000000000000000a
+ 5465737420555249203000000000000000000000000000000000000000000000
+ 0000000000000000000000000000000000000000000000000000000000000030
+ 1111111111111111111111111111111111111111111111111111111111111111
+ 1111111111111111111111111111111100000000000000000000000000000000
+
+ 0000000000000000000000000000000000000000000000000000000000000040
+ 0000000000000000000000000000000000000000000000000000000000000080
+ 000000000000000000000000000000000000000000000000000000000000000a
+ 5465737420555249203100000000000000000000000000000000000000000000
+ 000000000000000000000000000000000000000000000000000000000000002f
+ 2222222222222222222222222222222222222222222222222222222222222222
+ 2222222222222222222222222222220000000000000000000000000000000000
+
+ 0000000000000000000000000000000000000000000000000000000000000040
+ 0000000000000000000000000000000000000000000000000000000000000080
+ 000000000000000000000000000000000000000000000000000000000000000a
+ 5465737420555249203200000000000000000000000000000000000000000000
+ 0000000000000000000000000000000000000000000000000000000000000002
+ 3333000000000000000000000000000000000000000000000000000000000000
+ "
+ ),
+ );
+}
+
+#[test]
+// #[ignore = "reason"]
+fn encode_decode_tuple0_tuple1_uint8_tuple1_string_bytes_tuple1_uint8_bytes() {
+ let int = 0xff;
+ let by = bytes(vec![0x11, 0x22, 0x33]);
+ let string = "some string".to_string();
+
+ test_impl::<((u8,), (String, bytes), (u8, bytes))>(
+ 0xdeadbeef,
+ ((int,), (string.clone(), by.clone()), (int, by)),
+ &hex!(
+ "
+ deadbeef
+ 0000000000000000000000000000000000000000000000000000000000000020
+ 00000000000000000000000000000000000000000000000000000000000000ff
+ 0000000000000000000000000000000000000000000000000000000000000060
+ 0000000000000000000000000000000000000000000000000000000000000120
+ 0000000000000000000000000000000000000000000000000000000000000040
+ 0000000000000000000000000000000000000000000000000000000000000080
+ 000000000000000000000000000000000000000000000000000000000000000b
+ 736f6d6520737472696e67000000000000000000000000000000000000000000
+ 0000000000000000000000000000000000000000000000000000000000000003
+ 1122330000000000000000000000000000000000000000000000000000000000
+ 00000000000000000000000000000000000000000000000000000000000000ff
+ 0000000000000000000000000000000000000000000000000000000000000040
+ 0000000000000000000000000000000000000000000000000000000000000003
+ 1122330000000000000000000000000000000000000000000000000000000000
+ "
+ ),
+ );
+}
+
+#[test]
+fn encode_decode_tuple0_tuple1_uint8_tuple1_uint8_uint8_tuple1_uint8_uint8() {
+ test_impl::<((u8,), (u8, u8), (u8, u8))>(
+ 0xdeadbeef,
+ ((43,), (44, 45), (46, 47)),
+ &hex!(
+ "
+ deadbeef
+ 000000000000000000000000000000000000000000000000000000000000002b
+ 000000000000000000000000000000000000000000000000000000000000002c
+ 000000000000000000000000000000000000000000000000000000000000002d
+ 000000000000000000000000000000000000000000000000000000000000002e
+ 000000000000000000000000000000000000000000000000000000000000002f
+ "
+ ),
+ );
+}
+
+#[test]
+fn encode_decode_tuple0_tuple1_uint8_tuple1_uint8() {
+ test_impl::<((u8,), (u8,))>(
+ 0xdeadbeef,
+ ((43,), (44,)),
+ &hex!(
+ "
+ deadbeef
+ 000000000000000000000000000000000000000000000000000000000000002b
+ 000000000000000000000000000000000000000000000000000000000000002c
+ "
+ ),
+ );
+}
+
+#[test]
+fn encode_decode_tuple0_tuple1_uint8_uint8() {
+ test_impl::<((u8, u8),)>(
+ 0xdeadbeef,
+ ((43, 44),),
+ &hex!(
+ "
+ deadbeef
+ 000000000000000000000000000000000000000000000000000000000000002b
+ 000000000000000000000000000000000000000000000000000000000000002c
+ "
+ ),
+ );
+}
+
+#[test]
+fn encode_decode_tuple_uint8_uint8() {
+ test_impl::<(u8, u8)>(
+ 0xdeadbeef,
+ (43, 44),
+ &hex!(
+ "
+ deadbeef
+ 000000000000000000000000000000000000000000000000000000000000002b
+ 000000000000000000000000000000000000000000000000000000000000002c
+ "
+ ),
+ );
+}
+
+#[test]
+fn encode_decode_tuple0_tuple1_uint8_uint8_tuple1_uint8_uint8_and_uint8() {
+ test_impl::<((u8, u8), (u8, u8), u8)>(
+ 0xdeadbeef,
+ ((10, 11), (12, 13), 14),
+ &hex!(
+ "
+ deadbeef
+ 000000000000000000000000000000000000000000000000000000000000000a
+ 000000000000000000000000000000000000000000000000000000000000000b
+ 000000000000000000000000000000000000000000000000000000000000000c
+ 000000000000000000000000000000000000000000000000000000000000000d
+ 000000000000000000000000000000000000000000000000000000000000000e
+ "
),
- (
- "Test URI 1".to_string(),
- bytes(vec![
- 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
- 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
- 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
- 0x22, 0x22, 0x22, 0x22, 0x22
- ])
+ );
+}
+
+#[test]
+fn encode_decode_tuple0_tuple1_string() {
+ test_impl::<((String,),)>(
+ 0xdeadbeef,
+ (("some string".to_string(),),),
+ &hex!(
+ "
+ deadbeef
+ 0000000000000000000000000000000000000000000000000000000000000020
+ 0000000000000000000000000000000000000000000000000000000000000020
+ 0000000000000000000000000000000000000000000000000000000000000020
+ 000000000000000000000000000000000000000000000000000000000000000b
+ 736f6d6520737472696e67000000000000000000000000000000000000000000
+ "
),
- ("Test URI 2".to_string(), bytes(vec![0x33, 0x33])),
- ],
- &hex!(
- "
- deadbeef
- 0000000000000000000000000000000000000000000000000000000000000020
- 0000000000000000000000000000000000000000000000000000000000000003
-
- 0000000000000000000000000000000000000000000000000000000000000060
- 0000000000000000000000000000000000000000000000000000000000000140
- 0000000000000000000000000000000000000000000000000000000000000220
+ );
+}
- 0000000000000000000000000000000000000000000000000000000000000040
- 0000000000000000000000000000000000000000000000000000000000000080
- 000000000000000000000000000000000000000000000000000000000000000a
- 5465737420555249203000000000000000000000000000000000000000000000
- 0000000000000000000000000000000000000000000000000000000000000030
- 1111111111111111111111111111111111111111111111111111111111111111
- 1111111111111111111111111111111100000000000000000000000000000000
+#[test]
+fn encode_decode_tuple0_tuple1_uint8_string() {
+ test_impl::<((u8, String),)>(
+ 0xdeadbeef,
+ ((0xff, "some string".to_string()),),
+ &hex!(
+ "
+ deadbeef
+ 0000000000000000000000000000000000000000000000000000000000000020
+ 0000000000000000000000000000000000000000000000000000000000000020
+ 00000000000000000000000000000000000000000000000000000000000000ff
+ 0000000000000000000000000000000000000000000000000000000000000040
+ 000000000000000000000000000000000000000000000000000000000000000b
+ 736f6d6520737472696e67000000000000000000000000000000000000000000
+ "
+ ),
+ );
+}
- 0000000000000000000000000000000000000000000000000000000000000040
- 0000000000000000000000000000000000000000000000000000000000000080
- 000000000000000000000000000000000000000000000000000000000000000a
- 5465737420555249203100000000000000000000000000000000000000000000
- 000000000000000000000000000000000000000000000000000000000000002f
- 2222222222222222222222222222222222222222222222222222222222222222
- 2222222222222222222222222222220000000000000000000000000000000000
+#[test]
+fn encode_decode_tuple0_tuple1_string_bytes() {
+ test_impl::<((String, bytes),)>(
+ 0xdeadbeef,
+ (("some string".to_string(), bytes(vec![1, 2, 3])),),
+ &hex!(
+ "
+ deadbeef
+ 0000000000000000000000000000000000000000000000000000000000000020
+ 0000000000000000000000000000000000000000000000000000000000000020
+ 0000000000000000000000000000000000000000000000000000000000000040
+ 0000000000000000000000000000000000000000000000000000000000000080
+ 000000000000000000000000000000000000000000000000000000000000000b
+ 736f6d6520737472696e67000000000000000000000000000000000000000000
+ 0000000000000000000000000000000000000000000000000000000000000003
+ 0102030000000000000000000000000000000000000000000000000000000000
+ "
+ ),
+ );
+}
- 0000000000000000000000000000000000000000000000000000000000000040
- 0000000000000000000000000000000000000000000000000000000000000080
+#[test]
+fn encode_decode_tuple0_tuple1_uint8_tuple1_string() {
+ test_impl::<((u8,), (String,))>(
+ 0xdeadbeef,
+ ((0xff,), ("some string".to_string(),)),
+ &hex!(
+ "
+ deadbeef
+ 0000000000000000000000000000000000000000000000000000000000000020
+ 00000000000000000000000000000000000000000000000000000000000000ff
+ 0000000000000000000000000000000000000000000000000000000000000040
+ 0000000000000000000000000000000000000000000000000000000000000020
+ 000000000000000000000000000000000000000000000000000000000000000b
+ 736f6d6520737472696e67000000000000000000000000000000000000000000
+ "
+ ),
+ );
+}
+
+#[test]
+fn parse_multiple_params() {
+ let encoded_data = hex!(
+ "
+ deadbeef
000000000000000000000000000000000000000000000000000000000000000a
- 5465737420555249203200000000000000000000000000000000000000000000
- 0000000000000000000000000000000000000000000000000000000000000002
- 3333000000000000000000000000000000000000000000000000000000000000
+ 000000000000000000000000000000000000000000000000000000000000000b
"
- )
-);
+ );
+ let (_, mut decoder) = AbiReader::new_call(&encoded_data).unwrap();
+ let p1 = <u8>::abi_read(&mut decoder).unwrap();
+ let p2 = <u8>::abi_read(&mut decoder).unwrap();
+ assert_eq!(p1, 0x0a);
+ assert_eq!(p2, 0x0b);
+}
crates/evm-coder/src/abi/traits.rsdiffbeforeafterboth--- a/crates/evm-coder/src/abi/traits.rs
+++ b/crates/evm-coder/src/abi/traits.rs
@@ -22,12 +22,6 @@
fn size() -> usize;
}
-/// Sealed traits.
-pub mod sealed {
- /// Not all types can be placed in vec, i.e `Vec<u8>` is restricted, `bytes` should be used instead
- pub trait CanBePlacedInVec {}
-}
-
/// [`AbiReader`] implements reading of many types.
pub trait AbiRead {
/// Read item from current position, advanding decoder
@@ -47,11 +41,5 @@
let mut writer = AbiWriter::new();
self.abi_write(&mut writer);
Ok(writer.into())
- }
-}
-
-impl<T: AbiWrite> AbiWrite for &T {
- fn abi_write(&self, writer: &mut AbiWriter) {
- T::abi_write(self, writer);
}
}
crates/evm-coder/src/lib.rsdiffbeforeafterboth--- a/crates/evm-coder/src/lib.rs
+++ b/crates/evm-coder/src/lib.rs
@@ -93,6 +93,7 @@
pub use evm_coder_procedural::solidity;
/// See [`solidity_interface`]
pub use evm_coder_procedural::weight;
+pub use evm_coder_procedural::AbiCoder;
pub use sha3_const;
/// Derives [`ToLog`] for enum
@@ -111,6 +112,15 @@
#[cfg(feature = "stubgen")]
pub mod solidity;
+/// Sealed traits.
+pub mod sealed {
+ /// Not every type should be directly placed in vec.
+ /// Vec encoding is not memory efficient, as every item will be padded
+ /// to 32 bytes.
+ /// Instead you should use specialized types (`bytes` in case of `Vec<u8>`)
+ pub trait CanBePlacedInVec {}
+}
+
/// Solidity type definitions (aliases from solidity name to rust type)
/// To be used in [`solidity_interface`] definitions, to make sure there is no
/// type conflict between Rust code and generated definitions
@@ -119,7 +129,6 @@
#[cfg(not(feature = "std"))]
use alloc::{vec::Vec};
- use pallet_evm::account::CrossAccountId;
use primitive_types::{U256, H160, H256};
pub type address = H160;
@@ -137,7 +146,7 @@
#[cfg(feature = "std")]
pub type string = ::std::string::String;
- #[derive(Default, Debug, PartialEq)]
+ #[derive(Default, Debug, PartialEq, Eq, Clone)]
pub struct bytes(pub Vec<u8>);
/// Solidity doesn't have `void` type, however we have special implementation
@@ -185,73 +194,7 @@
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
- }
- }
-
- #[derive(Debug, Default)]
- pub struct EthCrossAccount {
- pub(crate) eth: address,
- pub(crate) sub: uint256,
- }
-
- impl EthCrossAccount {
- pub fn from_sub_cross_account<T>(cross_account_id: &T::CrossAccountId) -> Self
- where
- T: pallet_evm::Config,
- T::AccountId: AsRef<[u8; 32]>,
- {
- if cross_account_id.is_canonical_substrate() {
- Self {
- eth: Default::default(),
- sub: convert_cross_account_to_uint256::<T>(cross_account_id),
- }
- } else {
- Self {
- eth: *cross_account_id.as_eth(),
- sub: Default::default(),
- }
- }
}
-
- pub fn into_sub_cross_account<T>(&self) -> crate::execution::Result<T::CrossAccountId>
- where
- T: pallet_evm::Config,
- T::AccountId: From<[u8; 32]>,
- {
- if self.eth == Default::default() && self.sub == Default::default() {
- Err("All fields of cross account is zeroed".into())
- } else if self.eth == Default::default() {
- Ok(convert_uint256_to_cross_account::<T>(self.sub))
- } else if self.sub == Default::default() {
- Ok(T::CrossAccountId::from_eth(self.eth))
- } else {
- Err("All fields of cross account is non zeroed".into())
- }
- }
- }
-
- /// Convert `CrossAccountId` to `uint256`.
- pub fn convert_cross_account_to_uint256<T: pallet_evm::Config>(
- from: &T::CrossAccountId,
- ) -> uint256
- where
- T::AccountId: AsRef<[u8; 32]>,
- {
- let slice = from.as_sub().as_ref();
- uint256::from_big_endian(slice)
- }
-
- /// Convert `uint256` to `CrossAccountId`.
- pub fn convert_uint256_to_cross_account<T: pallet_evm::Config>(
- from: uint256,
- ) -> T::CrossAccountId
- where
- T::AccountId: From<[u8; 32]>,
- {
- let mut new_admin_arr = [0_u8; 32];
- from.to_big_endian(&mut new_admin_arr);
- let account_id = T::AccountId::from(new_admin_arr);
- T::CrossAccountId::from_sub(account_id)
}
#[derive(Debug, Default)]
crates/evm-coder/src/solidity.rsdiffbeforeafterboth--- a/crates/evm-coder/src/solidity.rs
+++ /dev/null
@@ -1,702 +0,0 @@
-// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
-// This file is part of Unique Network.
-
-// Unique Network is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Unique Network is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-
-//! Implementation detail of [`crate::solidity_interface`] macro code-generation.
-//! You should not rely on any public item from this module, as it is only intended to be used
-//! by procedural macro, API and output format may be changed at any time.
-//!
-//! Purpose of this module is to receive solidity contract definition in module-specified
-//! format, and then output string, representing interface of this contract in solidity language
-
-#[cfg(not(feature = "std"))]
-use alloc::{string::String, vec::Vec, collections::BTreeMap, format};
-#[cfg(feature = "std")]
-use std::collections::BTreeMap;
-use core::{
- fmt::{self, Write},
- marker::PhantomData,
- cell::{Cell, RefCell},
- cmp::Reverse,
-};
-use impl_trait_for_tuples::impl_for_tuples;
-use crate::{types::*, custom_signature::SignatureUnit};
-
-#[derive(Default)]
-pub struct TypeCollector {
- /// Code => id
- /// id ordering is required to perform topo-sort on the resulting data
- structs: RefCell<BTreeMap<string, usize>>,
- anonymous: RefCell<BTreeMap<Vec<string>, usize>>,
- id: Cell<usize>,
-}
-impl TypeCollector {
- pub fn new() -> Self {
- Self::default()
- }
- pub fn collect(&self, item: string) {
- let id = self.next_id();
- self.structs.borrow_mut().insert(item, id);
- }
- pub fn next_id(&self) -> usize {
- let v = self.id.get();
- self.id.set(v + 1);
- v
- }
- pub fn collect_tuple<T: SolidityTupleType>(&self) -> String {
- let names = T::names(self);
- if let Some(id) = self.anonymous.borrow().get(&names).cloned() {
- return format!("Tuple{}", id);
- }
- let id = self.next_id();
- let mut str = String::new();
- writeln!(str, "/// @dev anonymous struct").unwrap();
- writeln!(str, "struct Tuple{} {{", id).unwrap();
- for (i, name) in names.iter().enumerate() {
- writeln!(str, "\t{} field_{};", name, i).unwrap();
- }
- writeln!(str, "}}").unwrap();
- self.collect(str);
- self.anonymous.borrow_mut().insert(names, id);
- format!("Tuple{}", id)
- }
- pub fn collect_struct<T: StructCollect>(&self) -> String {
- self.collect(<T as StructCollect>::declaration());
- <T as StructCollect>::name()
- }
- pub fn finish(self) -> Vec<string> {
- let mut data = self.structs.into_inner().into_iter().collect::<Vec<_>>();
- data.sort_by_key(|(_, id)| Reverse(*id));
- data.into_iter().map(|(code, _)| code).collect()
- }
-}
-
-pub trait StructCollect: 'static {
- /// Structure name.
- fn name() -> String;
- /// Structure declaration.
- fn declaration() -> String;
-}
-
-pub trait SolidityTypeName: 'static {
- fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;
- /// "simple" types are stored inline, no `memory` modifier should be used in solidity
- fn is_simple() -> bool;
- fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;
- /// Specialization
- fn is_void() -> bool {
- false
- }
-}
-macro_rules! solidity_type_name {
- ($($ty:ty => $name:literal $simple:literal = $default:literal),* $(,)?) => {
- $(
- impl SolidityTypeName for $ty {
- fn solidity_name(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {
- write!(writer, $name)
- }
- fn is_simple() -> bool {
- $simple
- }
- fn solidity_default(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {
- write!(writer, $default)
- }
- }
- )*
- };
-}
-
-solidity_type_name! {
- uint8 => "uint8" true = "0",
- uint32 => "uint32" true = "0",
- uint64 => "uint64" true = "0",
- uint128 => "uint128" true = "0",
- uint256 => "uint256" true = "0",
- bytes4 => "bytes4" true = "bytes4(0)",
- address => "address" true = "0x0000000000000000000000000000000000000000",
- string => "string" false = "\"\"",
- bytes => "bytes" false = "hex\"\"",
- bool => "bool" true = "false",
-}
-impl SolidityTypeName for void {
- fn solidity_name(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {
- Ok(())
- }
- fn is_simple() -> bool {
- true
- }
- fn solidity_default(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {
- Ok(())
- }
- fn is_void() -> bool {
- true
- }
-}
-
-mod sealed {
- /// Not every type should be directly placed in vec.
- /// Vec encoding is not memory efficient, as every item will be padded
- /// to 32 bytes.
- /// Instead you should use specialized types (`bytes` in case of `Vec<u8>`)
- pub trait CanBePlacedInVec {}
-}
-
-impl sealed::CanBePlacedInVec for uint256 {}
-impl sealed::CanBePlacedInVec for string {}
-impl sealed::CanBePlacedInVec for address {}
-impl sealed::CanBePlacedInVec for EthCrossAccount {}
-impl sealed::CanBePlacedInVec for Property {}
-
-impl<T: SolidityTypeName + sealed::CanBePlacedInVec> SolidityTypeName for Vec<T> {
- fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
- T::solidity_name(writer, tc)?;
- write!(writer, "[]")
- }
- fn is_simple() -> bool {
- false
- }
- fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
- write!(writer, "new ")?;
- T::solidity_name(writer, tc)?;
- write!(writer, "[](0)")
- }
-}
-
-impl SolidityTupleType for EthCrossAccount {
- fn names(tc: &TypeCollector) -> Vec<string> {
- let mut collected = Vec::with_capacity(Self::len());
- {
- let mut out = string::new();
- address::solidity_name(&mut out, tc).expect("no fmt error");
- collected.push(out);
- }
- {
- let mut out = string::new();
- uint256::solidity_name(&mut out, tc).expect("no fmt error");
- collected.push(out);
- }
- collected
- }
-
- fn len() -> usize {
- 2
- }
-}
-
-impl SolidityTypeName for EthCrossAccount {
- fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
- write!(writer, "{}", tc.collect_struct::<Self>())
- }
-
- fn is_simple() -> bool {
- false
- }
-
- fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
- write!(writer, "{}(", tc.collect_struct::<Self>())?;
- address::solidity_default(writer, tc)?;
- write!(writer, ",")?;
- uint256::solidity_default(writer, tc)?;
- write!(writer, ")")
- }
-}
-
-impl StructCollect for EthCrossAccount {
- fn name() -> String {
- "EthCrossAccount".into()
- }
-
- fn declaration() -> String {
- let mut str = String::new();
- writeln!(str, "/// @dev Cross account struct").unwrap();
- writeln!(str, "struct {} {{", Self::name()).unwrap();
- writeln!(str, "\taddress eth;").unwrap();
- writeln!(str, "\tuint256 sub;").unwrap();
- writeln!(str, "}}").unwrap();
- str
- }
-}
-
-impl StructCollect for Property {
- fn name() -> String {
- "Property".into()
- }
-
- fn declaration() -> String {
- let mut str = String::new();
- writeln!(str, "/// @dev Property struct").unwrap();
- writeln!(str, "struct {} {{", Self::name()).unwrap();
- writeln!(str, "\tstring key;").unwrap();
- writeln!(str, "\tbytes value;").unwrap();
- writeln!(str, "}}").unwrap();
- str
- }
-}
-
-impl SolidityTypeName for Property {
- fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
- write!(writer, "{}", tc.collect_struct::<Self>())
- }
-
- fn is_simple() -> bool {
- false
- }
-
- fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
- write!(writer, "{}(", tc.collect_struct::<Self>())?;
- address::solidity_default(writer, tc)?;
- write!(writer, ",")?;
- uint256::solidity_default(writer, tc)?;
- write!(writer, ")")
- }
-}
-
-impl SolidityTupleType for Property {
- fn names(tc: &TypeCollector) -> Vec<string> {
- let mut collected = Vec::with_capacity(Self::len());
- {
- let mut out = string::new();
- string::solidity_name(&mut out, tc).expect("no fmt error");
- collected.push(out);
- }
- {
- let mut out = string::new();
- bytes::solidity_name(&mut out, tc).expect("no fmt error");
- collected.push(out);
- }
- collected
- }
-
- fn len() -> usize {
- 2
- }
-}
-
-pub trait SolidityTupleType {
- fn names(tc: &TypeCollector) -> Vec<String>;
- fn len() -> usize;
-}
-
-macro_rules! count {
- () => (0usize);
- ( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*));
-}
-
-macro_rules! impl_tuples {
- ($($ident:ident)+) => {
- impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {}
- impl<$($ident: SolidityTypeName + 'static),+> SolidityTupleType for ($($ident,)+) {
- fn names(tc: &TypeCollector) -> Vec<string> {
- let mut collected = Vec::with_capacity(Self::len());
- $({
- let mut out = string::new();
- $ident::solidity_name(&mut out, tc).expect("no fmt error");
- collected.push(out);
- })*;
- collected
- }
-
- fn len() -> usize {
- count!($($ident)*)
- }
- }
- impl<$($ident: SolidityTypeName + 'static),+> SolidityTypeName for ($($ident,)+) {
- fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
- write!(writer, "{}", tc.collect_tuple::<Self>())
- }
- fn is_simple() -> bool {
- false
- }
- #[allow(unused_assignments)]
- fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
- write!(writer, "{}(", tc.collect_tuple::<Self>())?;
- let mut first = true;
- $(
- if !first {
- write!(writer, ",")?;
- } else {
- first = false;
- }
- <$ident>::solidity_default(writer, tc)?;
- )*
- write!(writer, ")")
- }
- }
- };
-}
-
-impl_tuples! {A}
-impl_tuples! {A B}
-impl_tuples! {A B C}
-impl_tuples! {A B C D}
-impl_tuples! {A B C D E}
-impl_tuples! {A B C D E F}
-impl_tuples! {A B C D E F G}
-impl_tuples! {A B C D E F G H}
-impl_tuples! {A B C D E F G H I}
-impl_tuples! {A B C D E F G H I J}
-
-pub trait SolidityArguments {
- fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;
- fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result;
- fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;
- fn is_empty(&self) -> bool {
- self.len() == 0
- }
- fn len(&self) -> usize;
-}
-
-#[derive(Default)]
-pub struct UnnamedArgument<T>(PhantomData<*const T>);
-
-impl<T: SolidityTypeName> SolidityArguments for UnnamedArgument<T> {
- fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
- if !T::is_void() {
- T::solidity_name(writer, tc)?;
- if !T::is_simple() {
- write!(writer, " memory")?;
- }
- Ok(())
- } else {
- Ok(())
- }
- }
- fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result {
- Ok(())
- }
- fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
- T::solidity_default(writer, tc)
- }
- fn len(&self) -> usize {
- if T::is_void() {
- 0
- } else {
- 1
- }
- }
-}
-
-pub struct NamedArgument<T>(&'static str, PhantomData<*const T>);
-
-impl<T> NamedArgument<T> {
- pub fn new(name: &'static str) -> Self {
- Self(name, Default::default())
- }
-}
-
-impl<T: SolidityTypeName> SolidityArguments for NamedArgument<T> {
- fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
- if !T::is_void() {
- T::solidity_name(writer, tc)?;
- if !T::is_simple() {
- write!(writer, " memory")?;
- }
- write!(writer, " {}", self.0)
- } else {
- Ok(())
- }
- }
- fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {
- writeln!(writer, "\t{prefix}\t{};", self.0)
- }
- fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
- T::solidity_default(writer, tc)
- }
- fn len(&self) -> usize {
- if T::is_void() {
- 0
- } else {
- 1
- }
- }
-}
-
-pub struct SolidityEventArgument<T>(pub bool, &'static str, PhantomData<*const T>);
-
-impl<T> SolidityEventArgument<T> {
- pub fn new(indexed: bool, name: &'static str) -> Self {
- Self(indexed, name, Default::default())
- }
-}
-
-impl<T: SolidityTypeName> SolidityArguments for SolidityEventArgument<T> {
- fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
- if !T::is_void() {
- T::solidity_name(writer, tc)?;
- if self.0 {
- write!(writer, " indexed")?;
- }
- write!(writer, " {}", self.1)
- } else {
- Ok(())
- }
- }
- fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {
- writeln!(writer, "\t{prefix}\t{};", self.1)
- }
- fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
- T::solidity_default(writer, tc)
- }
- fn len(&self) -> usize {
- if T::is_void() {
- 0
- } else {
- 1
- }
- }
-}
-
-impl SolidityArguments for () {
- fn solidity_name(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {
- Ok(())
- }
- fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result {
- Ok(())
- }
- fn solidity_default(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {
- Ok(())
- }
- fn len(&self) -> usize {
- 0
- }
-}
-
-#[impl_for_tuples(1, 12)]
-impl SolidityArguments for Tuple {
- for_tuples!( where #( Tuple: SolidityArguments ),* );
-
- fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
- let mut first = true;
- for_tuples!( #(
- if !Tuple.is_empty() {
- if !first {
- write!(writer, ", ")?;
- }
- first = false;
- Tuple.solidity_name(writer, tc)?;
- }
- )* );
- Ok(())
- }
- fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {
- for_tuples!( #(
- Tuple.solidity_get(prefix, writer)?;
- )* );
- Ok(())
- }
- fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
- if self.is_empty() {
- Ok(())
- } else if self.len() == 1 {
- for_tuples!( #(
- Tuple.solidity_default(writer, tc)?;
- )* );
- Ok(())
- } else {
- write!(writer, "(")?;
- let mut first = true;
- for_tuples!( #(
- if !Tuple.is_empty() {
- if !first {
- write!(writer, ", ")?;
- }
- first = false;
- Tuple.solidity_default(writer, tc)?;
- }
- )* );
- write!(writer, ")")?;
- Ok(())
- }
- }
- fn len(&self) -> usize {
- for_tuples!( #( Tuple.len() )+* )
- }
-}
-
-pub trait SolidityFunctions {
- fn solidity_name(
- &self,
- is_impl: bool,
- writer: &mut impl fmt::Write,
- tc: &TypeCollector,
- ) -> fmt::Result;
-}
-
-pub enum SolidityMutability {
- Pure,
- View,
- Mutable,
-}
-pub struct SolidityFunction<A, R> {
- pub docs: &'static [&'static str],
- pub selector: u32,
- pub hide: bool,
- pub custom_signature: SignatureUnit,
- pub name: &'static str,
- pub args: A,
- pub result: R,
- pub mutability: SolidityMutability,
- pub is_payable: bool,
-}
-impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {
- fn solidity_name(
- &self,
- is_impl: bool,
- writer: &mut impl fmt::Write,
- tc: &TypeCollector,
- ) -> fmt::Result {
- let hide_comment = self.hide.then(|| "// ").unwrap_or("");
- for doc in self.docs {
- writeln!(writer, "\t{hide_comment}///{}", doc)?;
- }
- writeln!(
- writer,
- "\t{hide_comment}/// @dev EVM selector for this function is: 0x{:0>8x},",
- self.selector
- )?;
- writeln!(
- writer,
- "\t{hide_comment}/// or in textual repr: {}",
- self.custom_signature.as_str().expect("bad utf-8")
- )?;
- write!(writer, "\t{hide_comment}function {}(", self.name)?;
- self.args.solidity_name(writer, tc)?;
- write!(writer, ")")?;
- if is_impl {
- write!(writer, " public")?;
- } else {
- write!(writer, " external")?;
- }
- match &self.mutability {
- SolidityMutability::Pure => write!(writer, " pure")?,
- SolidityMutability::View => write!(writer, " view")?,
- SolidityMutability::Mutable => {}
- }
- if self.is_payable {
- write!(writer, " payable")?;
- }
- if !self.result.is_empty() {
- write!(writer, " returns (")?;
- self.result.solidity_name(writer, tc)?;
- write!(writer, ")")?;
- }
- if is_impl {
- writeln!(writer, " {{")?;
- writeln!(writer, "\t{hide_comment}\trequire(false, stub_error);")?;
- self.args.solidity_get(hide_comment, writer)?;
- match &self.mutability {
- SolidityMutability::Pure => {}
- SolidityMutability::View => writeln!(writer, "\t{hide_comment}\tdummy;")?,
- SolidityMutability::Mutable => writeln!(writer, "\t{hide_comment}\tdummy = 0;")?,
- }
- if !self.result.is_empty() {
- write!(writer, "\t{hide_comment}\treturn ")?;
- self.result.solidity_default(writer, tc)?;
- writeln!(writer, ";")?;
- }
- writeln!(writer, "\t{hide_comment}}}")?;
- } else {
- writeln!(writer, ";")?;
- }
- if self.hide {
- writeln!(writer, "// FORMATTING: FORCE NEWLINE")?;
- }
- Ok(())
- }
-}
-
-#[impl_for_tuples(0, 48)]
-impl SolidityFunctions for Tuple {
- for_tuples!( where #( Tuple: SolidityFunctions ),* );
-
- fn solidity_name(
- &self,
- is_impl: bool,
- writer: &mut impl fmt::Write,
- tc: &TypeCollector,
- ) -> fmt::Result {
- let mut first = false;
- for_tuples!( #(
- Tuple.solidity_name(is_impl, writer, tc)?;
- )* );
- Ok(())
- }
-}
-
-pub struct SolidityInterface<F: SolidityFunctions> {
- pub docs: &'static [&'static str],
- pub selector: bytes4,
- pub name: &'static str,
- pub is: &'static [&'static str],
- pub functions: F,
-}
-
-impl<F: SolidityFunctions> SolidityInterface<F> {
- pub fn format(
- &self,
- is_impl: bool,
- out: &mut impl fmt::Write,
- tc: &TypeCollector,
- ) -> fmt::Result {
- const ZERO_BYTES: [u8; 4] = [0; 4];
- for doc in self.docs {
- writeln!(out, "///{}", doc)?;
- }
- if self.selector != ZERO_BYTES {
- writeln!(
- out,
- "/// @dev the ERC-165 identifier for this interface is 0x{:0>8x}",
- u32::from_be_bytes(self.selector)
- )?;
- }
- if is_impl {
- write!(out, "contract ")?;
- } else {
- write!(out, "interface ")?;
- }
- write!(out, "{}", self.name)?;
- if !self.is.is_empty() {
- write!(out, " is")?;
- for (i, n) in self.is.iter().enumerate() {
- if i != 0 {
- write!(out, ",")?;
- }
- write!(out, " {}", n)?;
- }
- }
- writeln!(out, " {{")?;
- self.functions.solidity_name(is_impl, out, tc)?;
- writeln!(out, "}}")?;
- Ok(())
- }
-}
-
-pub struct SolidityEvent<A> {
- pub name: &'static str,
- pub args: A,
-}
-
-impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {
- fn solidity_name(
- &self,
- _is_impl: bool,
- writer: &mut impl fmt::Write,
- tc: &TypeCollector,
- ) -> fmt::Result {
- write!(writer, "\tevent {}(", self.name)?;
- self.args.solidity_name(writer, tc)?;
- writeln!(writer, ");")
- }
-}
crates/evm-coder/src/solidity/impls.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/evm-coder/src/solidity/impls.rs
@@ -0,0 +1,190 @@
+use super::{TypeCollector, SolidityTypeName, SolidityType, StructCollect};
+use crate::{sealed, types::*};
+use core::fmt;
+use primitive_types::{U256, H160};
+
+macro_rules! solidity_type_name {
+ ($($ty:ty => $name:literal $simple:literal = $default:literal),* $(,)?) => {
+ $(
+ impl SolidityTypeName for $ty {
+ fn solidity_name(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {
+ write!(writer, $name)
+ }
+ fn is_simple() -> bool {
+ $simple
+ }
+ fn solidity_default(writer: &mut impl core::fmt::Write, _tc: &TypeCollector) -> core::fmt::Result {
+ write!(writer, $default)
+ }
+ }
+
+ impl StructCollect for $ty {
+ fn name() -> String {
+ $name.to_string()
+ }
+
+ fn declaration() -> String {
+ String::default()
+ }
+ }
+ )*
+ };
+}
+
+solidity_type_name! {
+ u8 => "uint8" true = "0",
+ u32 => "uint32" true = "0",
+ u64 => "uint64" true = "0",
+ u128 => "uint128" true = "0",
+ U256 => "uint256" true = "0",
+ bytes4 => "bytes4" true = "bytes4(0)",
+ H160 => "address" true = "0x0000000000000000000000000000000000000000",
+ string => "string" false = "\"\"",
+ bytes => "bytes" false = "hex\"\"",
+ bool => "bool" true = "false",
+}
+
+impl SolidityTypeName for void {
+ fn solidity_name(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {
+ Ok(())
+ }
+ fn is_simple() -> bool {
+ true
+ }
+ fn solidity_default(_writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {
+ Ok(())
+ }
+ fn is_void() -> bool {
+ true
+ }
+}
+
+impl<T: SolidityTypeName + sealed::CanBePlacedInVec> SolidityTypeName for Vec<T> {
+ fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
+ T::solidity_name(writer, tc)?;
+ write!(writer, "[]")
+ }
+ fn is_simple() -> bool {
+ false
+ }
+ fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
+ write!(writer, "new ")?;
+ T::solidity_name(writer, tc)?;
+ write!(writer, "[](0)")
+ }
+}
+
+macro_rules! count {
+ () => (0usize);
+ ( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*));
+}
+
+macro_rules! impl_tuples {
+ ($($ident:ident)+) => {
+ impl<$($ident: SolidityTypeName + 'static),+> SolidityType for ($($ident,)+) {
+ fn names(tc: &TypeCollector) -> Vec<string> {
+ let mut collected = Vec::with_capacity(Self::len());
+ $({
+ let mut out = string::new();
+ $ident::solidity_name(&mut out, tc).expect("no fmt error");
+ collected.push(out);
+ })*;
+ collected
+ }
+
+ fn len() -> usize {
+ count!($($ident)*)
+ }
+ }
+ impl<$($ident: SolidityTypeName + 'static),+> SolidityTypeName for ($($ident,)+) {
+ fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
+ write!(writer, "{}", tc.collect_tuple::<Self>())
+ }
+ fn is_simple() -> bool {
+ false
+ }
+ #[allow(unused_assignments)]
+ fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
+ write!(writer, "{}(", tc.collect_tuple::<Self>())?;
+ let mut first = true;
+ $(
+ if !first {
+ write!(writer, ",")?;
+ } else {
+ first = false;
+ }
+ <$ident>::solidity_default(writer, tc)?;
+ )*
+ write!(writer, ")")
+ }
+ }
+ };
+}
+
+impl_tuples! {A}
+impl_tuples! {A B}
+impl_tuples! {A B C}
+impl_tuples! {A B C D}
+impl_tuples! {A B C D E}
+impl_tuples! {A B C D E F}
+impl_tuples! {A B C D E F G}
+impl_tuples! {A B C D E F G H}
+impl_tuples! {A B C D E F G H I}
+impl_tuples! {A B C D E F G H I J}
+
+impl StructCollect for Property {
+ fn name() -> String {
+ "Property".into()
+ }
+
+ fn declaration() -> String {
+ use std::fmt::Write;
+
+ let mut str = String::new();
+ writeln!(str, "/// @dev Property struct").unwrap();
+ writeln!(str, "struct {} {{", Self::name()).unwrap();
+ writeln!(str, "\tstring key;").unwrap();
+ writeln!(str, "\tbytes value;").unwrap();
+ writeln!(str, "}}").unwrap();
+ str
+ }
+}
+
+impl SolidityTypeName for Property {
+ fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
+ write!(writer, "{}", tc.collect_struct::<Self>())
+ }
+
+ fn is_simple() -> bool {
+ false
+ }
+
+ fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
+ write!(writer, "{}(", tc.collect_struct::<Self>())?;
+ address::solidity_default(writer, tc)?;
+ write!(writer, ",")?;
+ uint256::solidity_default(writer, tc)?;
+ write!(writer, ")")
+ }
+}
+
+impl SolidityType for Property {
+ fn names(tc: &TypeCollector) -> Vec<string> {
+ let mut collected = Vec::with_capacity(Self::len());
+ {
+ let mut out = string::new();
+ string::solidity_name(&mut out, tc).expect("no fmt error");
+ collected.push(out);
+ }
+ {
+ let mut out = string::new();
+ bytes::solidity_name(&mut out, tc).expect("no fmt error");
+ collected.push(out);
+ }
+ collected
+ }
+
+ fn len() -> usize {
+ 2
+ }
+}
crates/evm-coder/src/solidity/mod.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/evm-coder/src/solidity/mod.rs
@@ -0,0 +1,421 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+//! Implementation detail of [`crate::solidity_interface`] macro code-generation.
+//! You should not rely on any public item from this module, as it is only intended to be used
+//! by procedural macro, API and output format may be changed at any time.
+//!
+//! Purpose of this module is to receive solidity contract definition in module-specified
+//! format, and then output string, representing interface of this contract in solidity language
+
+mod traits;
+pub use traits::*;
+mod impls;
+
+#[cfg(not(feature = "std"))]
+use alloc::{string::String, vec::Vec, collections::BTreeMap, format};
+#[cfg(feature = "std")]
+use std::collections::BTreeMap;
+use core::{
+ fmt::{self, Write},
+ marker::PhantomData,
+ cell::{Cell, RefCell},
+ cmp::Reverse,
+};
+use impl_trait_for_tuples::impl_for_tuples;
+use crate::{types::*, custom_signature::SignatureUnit};
+
+#[derive(Default)]
+pub struct TypeCollector {
+ /// Code => id
+ /// id ordering is required to perform topo-sort on the resulting data
+ structs: RefCell<BTreeMap<string, usize>>,
+ anonymous: RefCell<BTreeMap<Vec<string>, usize>>,
+ id: Cell<usize>,
+}
+impl TypeCollector {
+ pub fn new() -> Self {
+ Self::default()
+ }
+ pub fn collect(&self, item: string) {
+ let id = self.next_id();
+ self.structs.borrow_mut().insert(item, id);
+ }
+ pub fn next_id(&self) -> usize {
+ let v = self.id.get();
+ self.id.set(v + 1);
+ v
+ }
+ pub fn collect_tuple<T: SolidityType>(&self) -> String {
+ let names = T::names(self);
+ if let Some(id) = self.anonymous.borrow().get(&names).cloned() {
+ return format!("Tuple{}", id);
+ }
+ let id = self.next_id();
+ let mut str = String::new();
+ writeln!(str, "/// @dev anonymous struct").unwrap();
+ writeln!(str, "struct Tuple{} {{", id).unwrap();
+ for (i, name) in names.iter().enumerate() {
+ writeln!(str, "\t{} field_{};", name, i).unwrap();
+ }
+ writeln!(str, "}}").unwrap();
+ self.collect(str);
+ self.anonymous.borrow_mut().insert(names, id);
+ format!("Tuple{}", id)
+ }
+ pub fn collect_struct<T: StructCollect>(&self) -> String {
+ self.collect(<T as StructCollect>::declaration());
+ <T as StructCollect>::name()
+ }
+ pub fn finish(self) -> Vec<string> {
+ let mut data = self.structs.into_inner().into_iter().collect::<Vec<_>>();
+ data.sort_by_key(|(_, id)| Reverse(*id));
+ data.into_iter().map(|(code, _)| code).collect()
+ }
+}
+#[derive(Default)]
+pub struct UnnamedArgument<T>(PhantomData<*const T>);
+
+impl<T: SolidityTypeName> SolidityArguments for UnnamedArgument<T> {
+ fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
+ if !T::is_void() {
+ T::solidity_name(writer, tc)?;
+ if !T::is_simple() {
+ write!(writer, " memory")?;
+ }
+ Ok(())
+ } else {
+ Ok(())
+ }
+ }
+ fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result {
+ Ok(())
+ }
+ fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
+ T::solidity_default(writer, tc)
+ }
+ fn len(&self) -> usize {
+ if T::is_void() {
+ 0
+ } else {
+ 1
+ }
+ }
+}
+
+pub struct NamedArgument<T>(&'static str, PhantomData<*const T>);
+
+impl<T> NamedArgument<T> {
+ pub fn new(name: &'static str) -> Self {
+ Self(name, Default::default())
+ }
+}
+
+impl<T: SolidityTypeName> SolidityArguments for NamedArgument<T> {
+ fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
+ if !T::is_void() {
+ T::solidity_name(writer, tc)?;
+ if !T::is_simple() {
+ write!(writer, " memory")?;
+ }
+ write!(writer, " {}", self.0)
+ } else {
+ Ok(())
+ }
+ }
+ fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {
+ writeln!(writer, "\t{prefix}\t{};", self.0)
+ }
+ fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
+ T::solidity_default(writer, tc)
+ }
+ fn len(&self) -> usize {
+ if T::is_void() {
+ 0
+ } else {
+ 1
+ }
+ }
+}
+
+pub struct SolidityEventArgument<T>(pub bool, &'static str, PhantomData<*const T>);
+
+impl<T> SolidityEventArgument<T> {
+ pub fn new(indexed: bool, name: &'static str) -> Self {
+ Self(indexed, name, Default::default())
+ }
+}
+
+impl<T: SolidityTypeName> SolidityArguments for SolidityEventArgument<T> {
+ fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
+ if !T::is_void() {
+ T::solidity_name(writer, tc)?;
+ if self.0 {
+ write!(writer, " indexed")?;
+ }
+ write!(writer, " {}", self.1)
+ } else {
+ Ok(())
+ }
+ }
+ fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {
+ writeln!(writer, "\t{prefix}\t{};", self.1)
+ }
+ fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
+ T::solidity_default(writer, tc)
+ }
+ fn len(&self) -> usize {
+ if T::is_void() {
+ 0
+ } else {
+ 1
+ }
+ }
+}
+
+impl SolidityArguments for () {
+ fn solidity_name(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {
+ Ok(())
+ }
+ fn solidity_get(&self, _prefix: &str, _writer: &mut impl fmt::Write) -> fmt::Result {
+ Ok(())
+ }
+ fn solidity_default(&self, _writer: &mut impl fmt::Write, _tc: &TypeCollector) -> fmt::Result {
+ Ok(())
+ }
+ fn len(&self) -> usize {
+ 0
+ }
+}
+
+#[impl_for_tuples(1, 12)]
+impl SolidityArguments for Tuple {
+ for_tuples!( where #( Tuple: SolidityArguments ),* );
+
+ fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
+ let mut first = true;
+ for_tuples!( #(
+ if !Tuple.is_empty() {
+ if !first {
+ write!(writer, ", ")?;
+ }
+ first = false;
+ Tuple.solidity_name(writer, tc)?;
+ }
+ )* );
+ Ok(())
+ }
+ fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result {
+ for_tuples!( #(
+ Tuple.solidity_get(prefix, writer)?;
+ )* );
+ Ok(())
+ }
+ fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
+ if self.is_empty() {
+ Ok(())
+ } else if self.len() == 1 {
+ for_tuples!( #(
+ Tuple.solidity_default(writer, tc)?;
+ )* );
+ Ok(())
+ } else {
+ write!(writer, "(")?;
+ let mut first = true;
+ for_tuples!( #(
+ if !Tuple.is_empty() {
+ if !first {
+ write!(writer, ", ")?;
+ }
+ first = false;
+ Tuple.solidity_default(writer, tc)?;
+ }
+ )* );
+ write!(writer, ")")?;
+ Ok(())
+ }
+ }
+ fn len(&self) -> usize {
+ for_tuples!( #( Tuple.len() )+* )
+ }
+}
+
+pub enum SolidityMutability {
+ Pure,
+ View,
+ Mutable,
+}
+pub struct SolidityFunction<A, R> {
+ pub docs: &'static [&'static str],
+ pub selector: u32,
+ pub hide: bool,
+ pub custom_signature: SignatureUnit,
+ pub name: &'static str,
+ pub args: A,
+ pub result: R,
+ pub mutability: SolidityMutability,
+ pub is_payable: bool,
+}
+impl<A: SolidityArguments, R: SolidityArguments> SolidityFunctions for SolidityFunction<A, R> {
+ fn solidity_name(
+ &self,
+ is_impl: bool,
+ writer: &mut impl fmt::Write,
+ tc: &TypeCollector,
+ ) -> fmt::Result {
+ let hide_comment = self.hide.then_some("// ").unwrap_or("");
+ for doc in self.docs {
+ writeln!(writer, "\t{hide_comment}///{}", doc)?;
+ }
+ writeln!(
+ writer,
+ "\t{hide_comment}/// @dev EVM selector for this function is: 0x{:0>8x},",
+ self.selector
+ )?;
+ writeln!(
+ writer,
+ "\t{hide_comment}/// or in textual repr: {}",
+ self.custom_signature.as_str().expect("bad utf-8")
+ )?;
+ write!(writer, "\t{hide_comment}function {}(", self.name)?;
+ self.args.solidity_name(writer, tc)?;
+ write!(writer, ")")?;
+ if is_impl {
+ write!(writer, " public")?;
+ } else {
+ write!(writer, " external")?;
+ }
+ match &self.mutability {
+ SolidityMutability::Pure => write!(writer, " pure")?,
+ SolidityMutability::View => write!(writer, " view")?,
+ SolidityMutability::Mutable => {}
+ }
+ if self.is_payable {
+ write!(writer, " payable")?;
+ }
+ if !self.result.is_empty() {
+ write!(writer, " returns (")?;
+ self.result.solidity_name(writer, tc)?;
+ write!(writer, ")")?;
+ }
+ if is_impl {
+ writeln!(writer, " {{")?;
+ writeln!(writer, "\t{hide_comment}\trequire(false, stub_error);")?;
+ self.args.solidity_get(hide_comment, writer)?;
+ match &self.mutability {
+ SolidityMutability::Pure => {}
+ SolidityMutability::View => writeln!(writer, "\t{hide_comment}\tdummy;")?,
+ SolidityMutability::Mutable => writeln!(writer, "\t{hide_comment}\tdummy = 0;")?,
+ }
+ if !self.result.is_empty() {
+ write!(writer, "\t{hide_comment}\treturn ")?;
+ self.result.solidity_default(writer, tc)?;
+ writeln!(writer, ";")?;
+ }
+ writeln!(writer, "\t{hide_comment}}}")?;
+ } else {
+ writeln!(writer, ";")?;
+ }
+ if self.hide {
+ writeln!(writer, "// FORMATTING: FORCE NEWLINE")?;
+ }
+ Ok(())
+ }
+}
+
+#[impl_for_tuples(0, 48)]
+impl SolidityFunctions for Tuple {
+ for_tuples!( where #( Tuple: SolidityFunctions ),* );
+
+ fn solidity_name(
+ &self,
+ is_impl: bool,
+ writer: &mut impl fmt::Write,
+ tc: &TypeCollector,
+ ) -> fmt::Result {
+ let mut first = false;
+ for_tuples!( #(
+ Tuple.solidity_name(is_impl, writer, tc)?;
+ )* );
+ Ok(())
+ }
+}
+
+pub struct SolidityInterface<F: SolidityFunctions> {
+ pub docs: &'static [&'static str],
+ pub selector: bytes4,
+ pub name: &'static str,
+ pub is: &'static [&'static str],
+ pub functions: F,
+}
+
+impl<F: SolidityFunctions> SolidityInterface<F> {
+ pub fn format(
+ &self,
+ is_impl: bool,
+ out: &mut impl fmt::Write,
+ tc: &TypeCollector,
+ ) -> fmt::Result {
+ const ZERO_BYTES: [u8; 4] = [0; 4];
+ for doc in self.docs {
+ writeln!(out, "///{}", doc)?;
+ }
+ if self.selector != ZERO_BYTES {
+ writeln!(
+ out,
+ "/// @dev the ERC-165 identifier for this interface is 0x{:0>8x}",
+ u32::from_be_bytes(self.selector)
+ )?;
+ }
+ if is_impl {
+ write!(out, "contract ")?;
+ } else {
+ write!(out, "interface ")?;
+ }
+ write!(out, "{}", self.name)?;
+ if !self.is.is_empty() {
+ write!(out, " is")?;
+ for (i, n) in self.is.iter().enumerate() {
+ if i != 0 {
+ write!(out, ",")?;
+ }
+ write!(out, " {}", n)?;
+ }
+ }
+ writeln!(out, " {{")?;
+ self.functions.solidity_name(is_impl, out, tc)?;
+ writeln!(out, "}}")?;
+ Ok(())
+ }
+}
+
+pub struct SolidityEvent<A> {
+ pub name: &'static str,
+ pub args: A,
+}
+
+impl<A: SolidityArguments> SolidityFunctions for SolidityEvent<A> {
+ fn solidity_name(
+ &self,
+ _is_impl: bool,
+ writer: &mut impl fmt::Write,
+ tc: &TypeCollector,
+ ) -> fmt::Result {
+ write!(writer, "\tevent {}(", self.name)?;
+ self.args.solidity_name(writer, tc)?;
+ writeln!(writer, ");")
+ }
+}
crates/evm-coder/src/solidity/traits.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/evm-coder/src/solidity/traits.rs
@@ -0,0 +1,48 @@
+use super::TypeCollector;
+use core::fmt;
+
+pub trait StructCollect: 'static {
+ /// Structure name.
+ fn name() -> String;
+ /// Structure declaration.
+ fn declaration() -> String;
+}
+
+pub trait SolidityEnum: 'static {
+ fn solidity_option(&self) -> &str;
+}
+
+pub trait SolidityTypeName: 'static {
+ fn solidity_name(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;
+ /// "simple" types are stored inline, no `memory` modifier should be used in solidity
+ fn is_simple() -> bool;
+ fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;
+ /// Specialization
+ fn is_void() -> bool {
+ false
+ }
+}
+
+pub trait SolidityType {
+ fn names(tc: &TypeCollector) -> Vec<String>;
+ fn len() -> usize;
+}
+
+pub trait SolidityArguments {
+ fn solidity_name(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;
+ fn solidity_get(&self, prefix: &str, writer: &mut impl fmt::Write) -> fmt::Result;
+ fn solidity_default(&self, writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result;
+ fn is_empty(&self) -> bool {
+ self.len() == 0
+ }
+ fn len(&self) -> usize;
+}
+
+pub trait SolidityFunctions {
+ fn solidity_name(
+ &self,
+ is_impl: bool,
+ writer: &mut impl fmt::Write,
+ tc: &TypeCollector,
+ ) -> fmt::Result;
+}
crates/evm-coder/tests/abi_derive_generation.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/evm-coder/tests/abi_derive_generation.rs
@@ -0,0 +1,841 @@
+mod test_struct {
+ use evm_coder_procedural::AbiCoder;
+ use evm_coder::types::bytes;
+
+ #[test]
+ fn empty_struct() {
+ let t = trybuild::TestCases::new();
+ t.compile_fail("tests/build_failed/abi_derive_struct_generation.rs");
+ }
+
+ #[derive(AbiCoder, PartialEq, Debug)]
+ struct TypeStruct1SimpleParam {
+ _a: u8,
+ }
+
+ #[derive(AbiCoder, PartialEq, Debug)]
+ struct TypeStruct1DynamicParam {
+ _a: String,
+ }
+
+ #[derive(AbiCoder, PartialEq, Debug)]
+ struct TypeStruct2SimpleParam {
+ _a: u8,
+ _b: u32,
+ }
+
+ #[derive(AbiCoder, PartialEq, Debug)]
+ struct TypeStruct2DynamicParam {
+ _a: String,
+ _b: bytes,
+ }
+
+ #[derive(AbiCoder, PartialEq, Debug)]
+ struct TypeStruct2MixedParam {
+ _a: u8,
+ _b: bytes,
+ }
+
+ #[derive(AbiCoder, PartialEq, Debug)]
+ struct TypeStruct1DerivedSimpleParam {
+ _a: TypeStruct1SimpleParam,
+ }
+
+ #[derive(AbiCoder, PartialEq, Debug)]
+ struct TypeStruct2DerivedSimpleParam {
+ _a: TypeStruct1SimpleParam,
+ _b: TypeStruct2SimpleParam,
+ }
+
+ #[derive(AbiCoder, PartialEq, Debug)]
+ struct TypeStruct1DerivedDynamicParam {
+ _a: TypeStruct1DynamicParam,
+ }
+
+ #[derive(AbiCoder, PartialEq, Debug)]
+ struct TypeStruct2DerivedDynamicParam {
+ _a: TypeStruct1DynamicParam,
+ _b: TypeStruct2DynamicParam,
+ }
+
+ /// Some docs
+ /// At multi
+ /// line
+ #[derive(AbiCoder, PartialEq, Debug)]
+ struct TypeStruct3DerivedMixedParam {
+ /// Docs for A
+ /// multi
+ /// line
+ _a: TypeStruct1SimpleParam,
+ /// Docs for B
+ _b: TypeStruct2DynamicParam,
+ /// Docs for C
+ _c: TypeStruct2MixedParam,
+ }
+
+ #[test]
+ #[cfg(feature = "stubgen")]
+ fn struct_collect_type_struct3_derived_mixed_param() {
+ assert_eq!(
+ <TypeStruct3DerivedMixedParam as ::evm_coder::solidity::StructCollect>::name(),
+ "TypeStruct3DerivedMixedParam"
+ );
+ similar_asserts::assert_eq!(
+ <TypeStruct3DerivedMixedParam as ::evm_coder::solidity::StructCollect>::declaration(),
+ r#"/// @dev Some docs
+/// At multi
+/// line
+struct TypeStruct3DerivedMixedParam {
+ /// @dev Docs for A
+ /// multi
+ /// line
+ TypeStruct1SimpleParam _a;
+ /// @dev Docs for B
+ TypeStruct2DynamicParam _b;
+ /// @dev Docs for C
+ TypeStruct2MixedParam _c;
+}
+"#
+ );
+ }
+
+ #[test]
+ fn impl_abi_type_signature() {
+ assert_eq!(
+ <TypeStruct1SimpleParam as evm_coder::abi::AbiType>::SIGNATURE
+ .as_str()
+ .unwrap(),
+ "(uint8)"
+ );
+ assert_eq!(
+ <TypeStruct1DynamicParam as evm_coder::abi::AbiType>::SIGNATURE
+ .as_str()
+ .unwrap(),
+ "(string)"
+ );
+ assert_eq!(
+ <TypeStruct2SimpleParam as evm_coder::abi::AbiType>::SIGNATURE
+ .as_str()
+ .unwrap(),
+ "(uint8,uint32)"
+ );
+ assert_eq!(
+ <TypeStruct2DynamicParam as evm_coder::abi::AbiType>::SIGNATURE
+ .as_str()
+ .unwrap(),
+ "(string,bytes)"
+ );
+ assert_eq!(
+ <TypeStruct2MixedParam as evm_coder::abi::AbiType>::SIGNATURE
+ .as_str()
+ .unwrap(),
+ "(uint8,bytes)"
+ );
+ assert_eq!(
+ <TypeStruct1DerivedSimpleParam as evm_coder::abi::AbiType>::SIGNATURE
+ .as_str()
+ .unwrap(),
+ "((uint8))"
+ );
+ assert_eq!(
+ <TypeStruct2DerivedSimpleParam as evm_coder::abi::AbiType>::SIGNATURE
+ .as_str()
+ .unwrap(),
+ "((uint8),(uint8,uint32))"
+ );
+ assert_eq!(
+ <TypeStruct1DerivedDynamicParam as evm_coder::abi::AbiType>::SIGNATURE
+ .as_str()
+ .unwrap(),
+ "((string))"
+ );
+ assert_eq!(
+ <TypeStruct2DerivedDynamicParam as evm_coder::abi::AbiType>::SIGNATURE
+ .as_str()
+ .unwrap(),
+ "((string),(string,bytes))"
+ );
+ assert_eq!(
+ <TypeStruct3DerivedMixedParam as evm_coder::abi::AbiType>::SIGNATURE
+ .as_str()
+ .unwrap(),
+ "((uint8),(string,bytes),(uint8,bytes))"
+ );
+ }
+
+ #[test]
+ fn impl_abi_type_is_dynamic() {
+ assert_eq!(
+ <TypeStruct1SimpleParam as evm_coder::abi::AbiType>::is_dynamic(),
+ false
+ );
+ assert_eq!(
+ <TypeStruct1DynamicParam as evm_coder::abi::AbiType>::is_dynamic(),
+ true
+ );
+ assert_eq!(
+ <TypeStruct2SimpleParam as evm_coder::abi::AbiType>::is_dynamic(),
+ false
+ );
+ assert_eq!(
+ <TypeStruct2DynamicParam as evm_coder::abi::AbiType>::is_dynamic(),
+ true
+ );
+ assert_eq!(
+ <TypeStruct2MixedParam as evm_coder::abi::AbiType>::is_dynamic(),
+ true
+ );
+ assert_eq!(
+ <TypeStruct1DerivedSimpleParam as evm_coder::abi::AbiType>::is_dynamic(),
+ false
+ );
+ assert_eq!(
+ <TypeStruct2DerivedSimpleParam as evm_coder::abi::AbiType>::is_dynamic(),
+ false
+ );
+ assert_eq!(
+ <TypeStruct1DerivedDynamicParam as evm_coder::abi::AbiType>::is_dynamic(),
+ true
+ );
+ assert_eq!(
+ <TypeStruct2DerivedDynamicParam as evm_coder::abi::AbiType>::is_dynamic(),
+ true
+ );
+ assert_eq!(
+ <TypeStruct3DerivedMixedParam as evm_coder::abi::AbiType>::is_dynamic(),
+ true
+ );
+ }
+
+ #[test]
+ fn impl_abi_type_size() {
+ const ABI_ALIGNMENT: usize = 32;
+ assert_eq!(
+ <TypeStruct1SimpleParam as evm_coder::abi::AbiType>::size(),
+ ABI_ALIGNMENT
+ );
+ assert_eq!(
+ <TypeStruct1DynamicParam as evm_coder::abi::AbiType>::size(),
+ ABI_ALIGNMENT
+ );
+ assert_eq!(
+ <TypeStruct2SimpleParam as evm_coder::abi::AbiType>::size(),
+ ABI_ALIGNMENT * 2
+ );
+ assert_eq!(
+ <TypeStruct2DynamicParam as evm_coder::abi::AbiType>::size(),
+ ABI_ALIGNMENT * 2
+ );
+ assert_eq!(
+ <TypeStruct2MixedParam as evm_coder::abi::AbiType>::size(),
+ ABI_ALIGNMENT * 2
+ );
+ assert_eq!(
+ <TypeStruct1DerivedSimpleParam as evm_coder::abi::AbiType>::size(),
+ ABI_ALIGNMENT
+ );
+ assert_eq!(
+ <TypeStruct2DerivedSimpleParam as evm_coder::abi::AbiType>::size(),
+ ABI_ALIGNMENT * 3
+ );
+ assert_eq!(
+ <TypeStruct1DerivedDynamicParam as evm_coder::abi::AbiType>::size(),
+ ABI_ALIGNMENT
+ );
+ assert_eq!(
+ <TypeStruct2DerivedDynamicParam as evm_coder::abi::AbiType>::size(),
+ ABI_ALIGNMENT * 3
+ );
+ assert_eq!(
+ <TypeStruct3DerivedMixedParam as evm_coder::abi::AbiType>::size(),
+ ABI_ALIGNMENT * 5
+ );
+ }
+
+ #[derive(AbiCoder, PartialEq, Debug)]
+ struct TupleStruct1SimpleParam(u8);
+
+ #[derive(AbiCoder, PartialEq, Debug)]
+ struct TupleStruct1DynamicParam(String);
+
+ #[derive(AbiCoder, PartialEq, Debug)]
+ struct TupleStruct2SimpleParam(u8, u32);
+
+ #[derive(AbiCoder, PartialEq, Debug)]
+ struct TupleStruct2DynamicParam(String, bytes);
+
+ #[derive(AbiCoder, PartialEq, Debug)]
+ struct TupleStruct2MixedParam(u8, bytes);
+
+ #[derive(AbiCoder, PartialEq, Debug)]
+ struct TupleStruct1DerivedSimpleParam(TupleStruct1SimpleParam);
+
+ #[derive(AbiCoder, PartialEq, Debug)]
+ struct TupleStruct2DerivedSimpleParam(TupleStruct1SimpleParam, TupleStruct2SimpleParam);
+
+ #[derive(AbiCoder, PartialEq, Debug)]
+ struct TupleStruct1DerivedDynamicParam(TupleStruct1DynamicParam);
+
+ #[derive(AbiCoder, PartialEq, Debug)]
+ struct TupleStruct2DerivedDynamicParam(TupleStruct1DynamicParam, TupleStruct2DynamicParam);
+
+ /// Some docs
+ /// At multi
+ /// line
+ #[derive(AbiCoder, PartialEq, Debug)]
+ struct TupleStruct3DerivedMixedParam(
+ /// Docs for A
+ /// multi
+ /// line
+ TupleStruct1SimpleParam,
+ TupleStruct2DynamicParam,
+ /// Docs for C
+ TupleStruct2MixedParam,
+ );
+
+ #[test]
+ #[cfg(feature = "stubgen")]
+ fn struct_collect_tuple_struct3_derived_mixed_param() {
+ assert_eq!(
+ <TupleStruct3DerivedMixedParam as ::evm_coder::solidity::StructCollect>::name(),
+ "TupleStruct3DerivedMixedParam"
+ );
+ similar_asserts::assert_eq!(
+ <TupleStruct3DerivedMixedParam as ::evm_coder::solidity::StructCollect>::declaration(),
+ r#"/// @dev Some docs
+/// At multi
+/// line
+struct TupleStruct3DerivedMixedParam {
+ /// @dev Docs for A
+ /// multi
+ /// line
+ TupleStruct1SimpleParam field0;
+ TupleStruct2DynamicParam field1;
+ /// @dev Docs for C
+ TupleStruct2MixedParam field2;
+}
+"#
+ );
+ }
+
+ #[test]
+ fn impl_abi_type_signature_same_for_structs() {
+ assert_eq!(
+ <TypeStruct1SimpleParam as evm_coder::abi::AbiType>::SIGNATURE
+ .as_str()
+ .unwrap(),
+ <TupleStruct1SimpleParam as evm_coder::abi::AbiType>::SIGNATURE
+ .as_str()
+ .unwrap()
+ );
+ assert_eq!(
+ <TypeStruct1DynamicParam as evm_coder::abi::AbiType>::SIGNATURE
+ .as_str()
+ .unwrap(),
+ <TupleStruct1DynamicParam as evm_coder::abi::AbiType>::SIGNATURE
+ .as_str()
+ .unwrap()
+ );
+ assert_eq!(
+ <TypeStruct2SimpleParam as evm_coder::abi::AbiType>::SIGNATURE
+ .as_str()
+ .unwrap(),
+ <TupleStruct2SimpleParam as evm_coder::abi::AbiType>::SIGNATURE
+ .as_str()
+ .unwrap()
+ );
+ assert_eq!(
+ <TypeStruct2DynamicParam as evm_coder::abi::AbiType>::SIGNATURE
+ .as_str()
+ .unwrap(),
+ <TupleStruct2DynamicParam as evm_coder::abi::AbiType>::SIGNATURE
+ .as_str()
+ .unwrap()
+ );
+ assert_eq!(
+ <TypeStruct2MixedParam as evm_coder::abi::AbiType>::SIGNATURE
+ .as_str()
+ .unwrap(),
+ <TupleStruct2MixedParam as evm_coder::abi::AbiType>::SIGNATURE
+ .as_str()
+ .unwrap(),
+ );
+ assert_eq!(
+ <TypeStruct1DerivedSimpleParam as evm_coder::abi::AbiType>::SIGNATURE
+ .as_str()
+ .unwrap(),
+ <TupleStruct1DerivedSimpleParam as evm_coder::abi::AbiType>::SIGNATURE
+ .as_str()
+ .unwrap(),
+ );
+ assert_eq!(
+ <TypeStruct2DerivedSimpleParam as evm_coder::abi::AbiType>::SIGNATURE
+ .as_str()
+ .unwrap(),
+ <TupleStruct2DerivedSimpleParam as evm_coder::abi::AbiType>::SIGNATURE
+ .as_str()
+ .unwrap(),
+ );
+ assert_eq!(
+ <TypeStruct1DerivedDynamicParam as evm_coder::abi::AbiType>::SIGNATURE
+ .as_str()
+ .unwrap(),
+ <TupleStruct1DerivedDynamicParam as evm_coder::abi::AbiType>::SIGNATURE
+ .as_str()
+ .unwrap(),
+ );
+ assert_eq!(
+ <TypeStruct2DerivedDynamicParam as evm_coder::abi::AbiType>::SIGNATURE
+ .as_str()
+ .unwrap(),
+ <TupleStruct2DerivedDynamicParam as evm_coder::abi::AbiType>::SIGNATURE
+ .as_str()
+ .unwrap(),
+ );
+ assert_eq!(
+ <TypeStruct3DerivedMixedParam as evm_coder::abi::AbiType>::SIGNATURE
+ .as_str()
+ .unwrap(),
+ <TupleStruct3DerivedMixedParam as evm_coder::abi::AbiType>::SIGNATURE
+ .as_str()
+ .unwrap(),
+ );
+ }
+
+ #[test]
+ fn impl_abi_type_is_dynamic_same_for_structs() {
+ assert_eq!(
+ <TypeStruct1SimpleParam as evm_coder::abi::AbiType>::is_dynamic(),
+ <TupleStruct1SimpleParam as evm_coder::abi::AbiType>::is_dynamic()
+ );
+ assert_eq!(
+ <TypeStruct1DynamicParam as evm_coder::abi::AbiType>::is_dynamic(),
+ <TupleStruct1DynamicParam as evm_coder::abi::AbiType>::is_dynamic()
+ );
+ assert_eq!(
+ <TypeStruct2SimpleParam as evm_coder::abi::AbiType>::is_dynamic(),
+ <TupleStruct2SimpleParam as evm_coder::abi::AbiType>::is_dynamic()
+ );
+ assert_eq!(
+ <TypeStruct2DynamicParam as evm_coder::abi::AbiType>::is_dynamic(),
+ <TupleStruct2DynamicParam as evm_coder::abi::AbiType>::is_dynamic()
+ );
+ assert_eq!(
+ <TypeStruct2MixedParam as evm_coder::abi::AbiType>::is_dynamic(),
+ <TupleStruct2MixedParam as evm_coder::abi::AbiType>::is_dynamic()
+ );
+ assert_eq!(
+ <TypeStruct1DerivedSimpleParam as evm_coder::abi::AbiType>::is_dynamic(),
+ <TupleStruct1DerivedSimpleParam as evm_coder::abi::AbiType>::is_dynamic()
+ );
+ assert_eq!(
+ <TypeStruct2DerivedSimpleParam as evm_coder::abi::AbiType>::is_dynamic(),
+ <TupleStruct2DerivedSimpleParam as evm_coder::abi::AbiType>::is_dynamic()
+ );
+ assert_eq!(
+ <TypeStruct1DerivedDynamicParam as evm_coder::abi::AbiType>::is_dynamic(),
+ <TupleStruct1DerivedDynamicParam as evm_coder::abi::AbiType>::is_dynamic()
+ );
+ assert_eq!(
+ <TypeStruct2DerivedDynamicParam as evm_coder::abi::AbiType>::is_dynamic(),
+ <TupleStruct2DerivedDynamicParam as evm_coder::abi::AbiType>::is_dynamic()
+ );
+ assert_eq!(
+ <TypeStruct3DerivedMixedParam as evm_coder::abi::AbiType>::is_dynamic(),
+ <TupleStruct3DerivedMixedParam as evm_coder::abi::AbiType>::is_dynamic()
+ );
+ }
+
+ #[test]
+ fn impl_abi_type_size_same_for_structs() {
+ assert_eq!(
+ <TypeStruct1SimpleParam as evm_coder::abi::AbiType>::size(),
+ <TupleStruct1SimpleParam as evm_coder::abi::AbiType>::size()
+ );
+ assert_eq!(
+ <TypeStruct1DynamicParam as evm_coder::abi::AbiType>::size(),
+ <TupleStruct1DynamicParam as evm_coder::abi::AbiType>::size()
+ );
+ assert_eq!(
+ <TypeStruct2SimpleParam as evm_coder::abi::AbiType>::size(),
+ <TupleStruct2SimpleParam as evm_coder::abi::AbiType>::size()
+ );
+ assert_eq!(
+ <TypeStruct2DynamicParam as evm_coder::abi::AbiType>::size(),
+ <TupleStruct2DynamicParam as evm_coder::abi::AbiType>::size()
+ );
+ assert_eq!(
+ <TypeStruct2MixedParam as evm_coder::abi::AbiType>::size(),
+ <TupleStruct2MixedParam as evm_coder::abi::AbiType>::size()
+ );
+ assert_eq!(
+ <TypeStruct1DerivedSimpleParam as evm_coder::abi::AbiType>::size(),
+ <TupleStruct1DerivedSimpleParam as evm_coder::abi::AbiType>::size()
+ );
+ assert_eq!(
+ <TypeStruct2DerivedSimpleParam as evm_coder::abi::AbiType>::size(),
+ <TupleStruct2DerivedSimpleParam as evm_coder::abi::AbiType>::size()
+ );
+ assert_eq!(
+ <TypeStruct1DerivedDynamicParam as evm_coder::abi::AbiType>::size(),
+ <TupleStruct1DerivedDynamicParam as evm_coder::abi::AbiType>::size()
+ );
+ assert_eq!(
+ <TypeStruct2DerivedDynamicParam as evm_coder::abi::AbiType>::size(),
+ <TupleStruct2DerivedDynamicParam as evm_coder::abi::AbiType>::size()
+ );
+ assert_eq!(
+ <TypeStruct3DerivedMixedParam as evm_coder::abi::AbiType>::size(),
+ <TupleStruct3DerivedMixedParam as evm_coder::abi::AbiType>::size()
+ );
+ }
+
+ const FUNCTION_IDENTIFIER: u32 = 0xdeadbeef;
+
+ fn test_impl<Tuple, TupleStruct, TypeStruct>(
+ tuple_data: Tuple,
+ tuple_struct_data: TupleStruct,
+ type_struct_data: TypeStruct,
+ ) where
+ TypeStruct: evm_coder::abi::AbiWrite
+ + evm_coder::abi::AbiRead
+ + std::cmp::PartialEq
+ + std::fmt::Debug,
+ TupleStruct: evm_coder::abi::AbiWrite
+ + evm_coder::abi::AbiRead
+ + std::cmp::PartialEq
+ + std::fmt::Debug,
+ Tuple: evm_coder::abi::AbiWrite
+ + evm_coder::abi::AbiRead
+ + std::cmp::PartialEq
+ + std::fmt::Debug,
+ {
+ let encoded_type_struct = test_abi_write_impl(&type_struct_data);
+ let encoded_tuple_struct = test_abi_write_impl(&tuple_struct_data);
+ let encoded_tuple = test_abi_write_impl(&tuple_data);
+
+ similar_asserts::assert_eq!(encoded_tuple, encoded_type_struct);
+ similar_asserts::assert_eq!(encoded_tuple, encoded_tuple_struct);
+
+ {
+ let (_, mut decoder) = evm_coder::abi::AbiReader::new_call(&encoded_tuple).unwrap();
+ let restored_struct_data = <TypeStruct>::abi_read(&mut decoder).unwrap();
+ assert_eq!(restored_struct_data, type_struct_data);
+ }
+ {
+ let (_, mut decoder) = evm_coder::abi::AbiReader::new_call(&encoded_tuple).unwrap();
+ let restored_struct_data = <TupleStruct>::abi_read(&mut decoder).unwrap();
+ assert_eq!(restored_struct_data, tuple_struct_data);
+ }
+
+ {
+ let (_, mut decoder) =
+ evm_coder::abi::AbiReader::new_call(&encoded_type_struct).unwrap();
+ let restored_tuple_data = <Tuple>::abi_read(&mut decoder).unwrap();
+ assert_eq!(restored_tuple_data, tuple_data);
+ }
+ {
+ let (_, mut decoder) =
+ evm_coder::abi::AbiReader::new_call(&encoded_tuple_struct).unwrap();
+ let restored_tuple_data = <Tuple>::abi_read(&mut decoder).unwrap();
+ assert_eq!(restored_tuple_data, tuple_data);
+ }
+ }
+
+ fn test_abi_write_impl<A>(data: &A) -> Vec<u8>
+ where
+ A: evm_coder::abi::AbiWrite
+ + evm_coder::abi::AbiRead
+ + std::cmp::PartialEq
+ + std::fmt::Debug,
+ {
+ let mut writer = evm_coder::abi::AbiWriter::new_call(FUNCTION_IDENTIFIER);
+ data.abi_write(&mut writer);
+ let encoded_tuple = writer.finish();
+ encoded_tuple
+ }
+
+ #[test]
+ fn codec_struct_1_simple() {
+ let _a = 0xff;
+ test_impl::<(u8,), TupleStruct1SimpleParam, TypeStruct1SimpleParam>(
+ (_a,),
+ TupleStruct1SimpleParam(_a),
+ TypeStruct1SimpleParam { _a },
+ );
+ }
+
+ #[test]
+ fn codec_struct_1_dynamic() {
+ let _a: String = "some string".into();
+ test_impl::<(String,), TupleStruct1DynamicParam, TypeStruct1DynamicParam>(
+ (_a.clone(),),
+ TupleStruct1DynamicParam(_a.clone()),
+ TypeStruct1DynamicParam { _a },
+ );
+ }
+
+ #[test]
+ fn codec_struct_1_derived_simple() {
+ let _a: u8 = 0xff;
+ test_impl::<((u8,),), TupleStruct1DerivedSimpleParam, TypeStruct1DerivedSimpleParam>(
+ ((_a,),),
+ TupleStruct1DerivedSimpleParam(TupleStruct1SimpleParam(_a)),
+ TypeStruct1DerivedSimpleParam {
+ _a: TypeStruct1SimpleParam { _a },
+ },
+ );
+ }
+
+ #[test]
+ fn codec_struct_1_derived_dynamic() {
+ let _a: String = "some string".into();
+ test_impl::<((String,),), TupleStruct1DerivedDynamicParam, TypeStruct1DerivedDynamicParam>(
+ ((_a.clone(),),),
+ TupleStruct1DerivedDynamicParam(TupleStruct1DynamicParam(_a.clone())),
+ TypeStruct1DerivedDynamicParam {
+ _a: TypeStruct1DynamicParam { _a },
+ },
+ );
+ }
+
+ #[test]
+ fn codec_struct_2_simple() {
+ let _a = 0xff;
+ let _b = 0xbeefbaba;
+ test_impl::<(u8, u32), TupleStruct2SimpleParam, TypeStruct2SimpleParam>(
+ (_a, _b),
+ TupleStruct2SimpleParam(_a, _b),
+ TypeStruct2SimpleParam { _a, _b },
+ );
+ }
+
+ #[test]
+ fn codec_struct_2_dynamic() {
+ let _a: String = "some string".into();
+ let _b: bytes = bytes(vec![0x11, 0x22, 0x33]);
+ test_impl::<(String, bytes), TupleStruct2DynamicParam, TypeStruct2DynamicParam>(
+ (_a.clone(), _b.clone()),
+ TupleStruct2DynamicParam(_a.clone(), _b.clone()),
+ TypeStruct2DynamicParam { _a, _b },
+ );
+ }
+
+ #[test]
+ fn codec_struct_2_mixed() {
+ let _a: u8 = 0xff;
+ let _b: bytes = bytes(vec![0x11, 0x22, 0x33]);
+ test_impl::<(u8, bytes), TupleStruct2MixedParam, TypeStruct2MixedParam>(
+ (_a.clone(), _b.clone()),
+ TupleStruct2MixedParam(_a.clone(), _b.clone()),
+ TypeStruct2MixedParam { _a, _b },
+ );
+ }
+
+ #[test]
+ fn codec_struct_2_derived_simple() {
+ let _a = 0xff;
+ let _b = 0xbeefbaba;
+ test_impl::<
+ ((u8,), (u8, u32)),
+ TupleStruct2DerivedSimpleParam,
+ TypeStruct2DerivedSimpleParam,
+ >(
+ ((_a,), (_a, _b)),
+ TupleStruct2DerivedSimpleParam(
+ TupleStruct1SimpleParam(_a),
+ TupleStruct2SimpleParam(_a, _b),
+ ),
+ TypeStruct2DerivedSimpleParam {
+ _a: TypeStruct1SimpleParam { _a },
+ _b: TypeStruct2SimpleParam { _a, _b },
+ },
+ );
+ }
+
+ #[test]
+ fn codec_struct_2_derived_dynamic() {
+ let _a = "some string".to_string();
+ let _b = bytes(vec![0x11, 0x22, 0x33]);
+ test_impl::<
+ ((String,), (String, bytes)),
+ TupleStruct2DerivedDynamicParam,
+ TypeStruct2DerivedDynamicParam,
+ >(
+ ((_a.clone(),), (_a.clone(), _b.clone())),
+ TupleStruct2DerivedDynamicParam(
+ TupleStruct1DynamicParam(_a.clone()),
+ TupleStruct2DynamicParam(_a.clone(), _b.clone()),
+ ),
+ TypeStruct2DerivedDynamicParam {
+ _a: TypeStruct1DynamicParam { _a: _a.clone() },
+ _b: TypeStruct2DynamicParam { _a, _b },
+ },
+ );
+ }
+
+ #[test]
+ fn codec_struct_3_derived_mixed() {
+ let int = 0xff;
+ let by = bytes(vec![0x11, 0x22, 0x33]);
+ let string = "some string".to_string();
+ test_impl::<
+ ((u8,), (String, bytes), (u8, bytes)),
+ TupleStruct3DerivedMixedParam,
+ TypeStruct3DerivedMixedParam,
+ >(
+ ((int,), (string.clone(), by.clone()), (int, by.clone())),
+ TupleStruct3DerivedMixedParam(
+ TupleStruct1SimpleParam(int),
+ TupleStruct2DynamicParam(string.clone(), by.clone()),
+ TupleStruct2MixedParam(int, by.clone()),
+ ),
+ TypeStruct3DerivedMixedParam {
+ _a: TypeStruct1SimpleParam { _a: int },
+ _b: TypeStruct2DynamicParam {
+ _a: string.clone(),
+ _b: by.clone(),
+ },
+ _c: TypeStruct2MixedParam { _a: int, _b: by },
+ },
+ );
+ }
+
+ #[derive(AbiCoder, PartialEq, Debug)]
+ struct TypeStruct2SimpleStruct1Simple {
+ _a: TypeStruct2SimpleParam,
+ _b: TypeStruct2SimpleParam,
+ _c: u8,
+ }
+ #[derive(AbiCoder, PartialEq, Debug)]
+ struct TupleStruct2SimpleStruct1Simple(TupleStruct2SimpleParam, TupleStruct2SimpleParam, u8);
+
+ #[test]
+ fn codec_struct_2_struct_simple_1_simple() {
+ let _a = 0xff;
+ let _b = 0xbeefbaba;
+ test_impl::<
+ ((u8, u32), (u8, u32), u8),
+ TupleStruct2SimpleStruct1Simple,
+ TypeStruct2SimpleStruct1Simple,
+ >(
+ ((_a, _b), (_a, _b), _a),
+ TupleStruct2SimpleStruct1Simple(
+ TupleStruct2SimpleParam(_a, _b),
+ TupleStruct2SimpleParam(_a, _b),
+ _a,
+ ),
+ TypeStruct2SimpleStruct1Simple {
+ _a: TypeStruct2SimpleParam { _a, _b },
+ _b: TypeStruct2SimpleParam { _a, _b },
+ _c: _a,
+ },
+ );
+ }
+}
+
+mod test_enum {
+ use evm_coder::AbiCoder;
+
+ /// Some docs
+ /// At multi
+ /// line
+ #[derive(AbiCoder, Debug, PartialEq, Default)]
+ #[repr(u8)]
+ enum Color {
+ /// Docs for Red
+ /// multi
+ /// line
+ Red,
+ Green,
+ /// Docs for Blue
+ #[default]
+ Blue,
+ }
+
+ #[test]
+ fn empty() {}
+
+ #[test]
+ fn bad_enums() {
+ let t = trybuild::TestCases::new();
+ t.compile_fail("tests/build_failed/abi_derive_enum_generation.rs");
+ }
+
+ #[test]
+ fn impl_abi_type_signature_same_for_structs() {
+ assert_eq!(
+ <Color as evm_coder::abi::AbiType>::SIGNATURE
+ .as_str()
+ .unwrap(),
+ <u8 as evm_coder::abi::AbiType>::SIGNATURE.as_str().unwrap()
+ );
+ }
+
+ #[test]
+ fn impl_abi_type_is_dynamic_same_for_structs() {
+ assert_eq!(
+ <Color as evm_coder::abi::AbiType>::is_dynamic(),
+ <u8 as evm_coder::abi::AbiType>::is_dynamic()
+ );
+ }
+
+ #[test]
+ fn impl_abi_type_size_same_for_structs() {
+ assert_eq!(
+ <Color as evm_coder::abi::AbiType>::size(),
+ <u8 as evm_coder::abi::AbiType>::size()
+ );
+ }
+
+ #[test]
+ fn test_coder() {
+ const FUNCTION_IDENTIFIER: u32 = 0xdeadbeef;
+
+ let encoded_enum = {
+ let mut writer = evm_coder::abi::AbiWriter::new_call(FUNCTION_IDENTIFIER);
+ <Color as evm_coder::abi::AbiWrite>::abi_write(&Color::Green, &mut writer);
+ writer.finish()
+ };
+
+ let encoded_u8 = {
+ let mut writer = evm_coder::abi::AbiWriter::new_call(FUNCTION_IDENTIFIER);
+ <u8 as evm_coder::abi::AbiWrite>::abi_write(&(Color::Green as u8), &mut writer);
+ writer.finish()
+ };
+
+ similar_asserts::assert_eq!(encoded_enum, encoded_u8);
+
+ {
+ let (_, mut decoder) = evm_coder::abi::AbiReader::new_call(&encoded_enum).unwrap();
+ let restored_enum_data =
+ <Color as evm_coder::abi::AbiRead>::abi_read(&mut decoder).unwrap();
+ assert_eq!(restored_enum_data, Color::Green);
+ }
+ }
+
+ #[test]
+ #[cfg(feature = "stubgen")]
+ fn struct_collect_enum() {
+ assert_eq!(
+ <Color as ::evm_coder::solidity::StructCollect>::name(),
+ "Color"
+ );
+ similar_asserts::assert_eq!(
+ <Color as ::evm_coder::solidity::StructCollect>::declaration(),
+ r#"/// @dev Some docs
+/// At multi
+/// line
+enum Color {
+ /// @dev Docs for Red
+ /// multi
+ /// line
+ Red,
+ Green,
+ /// @dev Docs for Blue
+ Blue
+}
+"#
+ );
+ }
+}
crates/evm-coder/tests/build_failed/abi_derive_enum_generation.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/evm-coder/tests/build_failed/abi_derive_enum_generation.rs
@@ -0,0 +1,36 @@
+use evm_coder_procedural::AbiCoder;
+
+#[derive(AbiCoder)]
+enum NonRepr {
+ A,
+ B,
+ C,
+}
+
+#[derive(AbiCoder)]
+#[repr(u32)]
+enum NonReprU8 {
+ A,
+ B,
+ C,
+}
+
+#[derive(AbiCoder)]
+#[repr(u8)]
+enum RustEnum {
+ A(u128),
+ B,
+ C,
+}
+
+#[derive(AbiCoder)]
+#[repr(u8)]
+enum WithExplicit {
+ A = 128,
+ B,
+ C,
+}
+
+fn main() {
+ assert!(false);
+}
crates/evm-coder/tests/build_failed/abi_derive_enum_generation.stderrdiffbeforeafterboth--- /dev/null
+++ b/crates/evm-coder/tests/build_failed/abi_derive_enum_generation.stderr
@@ -0,0 +1,23 @@
+error: Enum is not "repr(u8)"
+ --> tests/build_failed/abi_derive_enum_generation.rs:4:6
+ |
+4 | enum NonRepr {
+ | ^^^^^^^
+
+error: Enum is not "repr(u8)"
+ --> tests/build_failed/abi_derive_enum_generation.rs:11:8
+ |
+11 | #[repr(u32)]
+ | ^^^
+
+error: Enumeration parameters should not have fields
+ --> tests/build_failed/abi_derive_enum_generation.rs:21:2
+ |
+21 | A(u128),
+ | ^
+
+error: Enumeration options should not have an explicit specified value
+ --> tests/build_failed/abi_derive_enum_generation.rs:29:2
+ |
+29 | A = 128,
+ | ^
crates/evm-coder/tests/build_failed/abi_derive_struct_generation.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/evm-coder/tests/build_failed/abi_derive_struct_generation.rs
@@ -0,0 +1,11 @@
+use evm_coder_procedural::AbiCoder;
+
+#[derive(AbiCoder, PartialEq, Debug)]
+struct EmptyStruct {}
+
+#[derive(AbiCoder, PartialEq, Debug)]
+struct EmptyTupleStruct();
+
+fn main() {
+ assert!(false);
+}
crates/evm-coder/tests/build_failed/abi_derive_struct_generation.stderrdiffbeforeafterboth--- /dev/null
+++ b/crates/evm-coder/tests/build_failed/abi_derive_struct_generation.stderr
@@ -0,0 +1,11 @@
+error: Empty structs not supported
+ --> tests/build_failed/abi_derive_struct_generation.rs:4:8
+ |
+4 | struct EmptyStruct {}
+ | ^^^^^^^^^^^
+
+error: Empty structs not supported
+ --> tests/build_failed/abi_derive_struct_generation.rs:7:8
+ |
+7 | struct EmptyTupleStruct();
+ | ^^^^^^^^^^^^^^^^
pallets/common/src/erc.rsdiffbeforeafterboth--- a/pallets/common/src/erc.rs
+++ b/pallets/common/src/erc.rs
@@ -16,6 +16,7 @@
//! This module contains the implementation of pallet methods for evm.
+pub use pallet_evm::{PrecompileOutput, PrecompileResult, PrecompileHandle, account::CrossAccountId};
use evm_coder::{
abi::AbiType,
solidity_interface, solidity, ToLog,
@@ -24,7 +25,6 @@
execution::{Result, Error},
weight,
};
-pub use pallet_evm::{PrecompileOutput, PrecompileResult, PrecompileHandle, account::CrossAccountId};
use pallet_evm_coder_substrate::dispatch_to_evm;
use sp_std::vec::Vec;
use up_data_structs::{
@@ -35,7 +35,8 @@
use crate::{
Pallet, CollectionHandle, Config, CollectionProperties, SelfWeightOf,
- eth::convert_cross_account_to_uint256, weights::WeightInfo,
+ eth::{EthCrossAccount, convert_cross_account_to_uint256},
+ weights::WeightInfo,
};
/// Events for ethereum collection helper.
pallets/common/src/eth.rsdiffbeforeafterboth--- a/pallets/common/src/eth.rs
+++ b/pallets/common/src/eth.rs
@@ -16,7 +16,10 @@
//! The module contains a number of functions for converting and checking ethereum identifiers.
-use evm_coder::types::{uint256, address};
+use evm_coder::{
+ AbiCoder,
+ types::{uint256, address},
+};
pub use pallet_evm::{Config, account::CrossAccountId};
use sp_core::H160;
use up_data_structs::CollectionId;
@@ -109,3 +112,46 @@
Err("All fields of cross account is non zeroed".into())
}
}
+
+/// Cross account struct
+#[derive(Debug, Default, AbiCoder)]
+pub struct EthCrossAccount {
+ pub(crate) eth: address,
+ pub(crate) sub: uint256,
+}
+
+impl EthCrossAccount {
+ pub fn from_sub_cross_account<T>(cross_account_id: &T::CrossAccountId) -> Self
+ where
+ T: pallet_evm::Config,
+ T::AccountId: AsRef<[u8; 32]>,
+ {
+ if cross_account_id.is_canonical_substrate() {
+ Self {
+ eth: Default::default(),
+ sub: convert_cross_account_to_uint256::<T>(cross_account_id),
+ }
+ } else {
+ Self {
+ eth: *cross_account_id.as_eth(),
+ sub: Default::default(),
+ }
+ }
+ }
+
+ pub fn into_sub_cross_account<T>(&self) -> evm_coder::execution::Result<T::CrossAccountId>
+ where
+ T: pallet_evm::Config,
+ T::AccountId: From<[u8; 32]>,
+ {
+ if self.eth == Default::default() && self.sub == Default::default() {
+ Err("All fields of cross account is zeroed".into())
+ } else if self.eth == Default::default() {
+ Ok(convert_uint256_to_cross_account::<T>(self.sub))
+ } else if self.sub == Default::default() {
+ Ok(T::CrossAccountId::from_eth(self.eth))
+ } else {
+ Err("All fields of cross account is non zeroed".into())
+ }
+ }
+}
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -376,6 +376,7 @@
type TreasuryAccountId: Get<Self::AccountId>;
/// Address under which the CollectionHelper contract would be available.
+ #[pallet::constant]
type ContractAddress: Get<H160>;
/// Mapper for token addresses to Ethereum addresses.
pallets/evm-contract-helpers/src/lib.rsdiffbeforeafterboth47 type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;47 type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;484849 /// Address, under which magic contract will be available49 /// Address, under which magic contract will be available50 #[pallet::constant]50 type ContractAddress: Get<H160>;51 type ContractAddress: Get<H160>;515252 /// In case of enabled sponsoring, but no sponsoring rate limit set,53 /// In case of enabled sponsoring, but no sponsoring rate limit set,pallets/evm-contract-helpers/src/stubs/ContractHelpers.rawdiffbeforeafterbothbinary blob — no preview
pallets/fungible/src/erc.rsdiffbeforeafterboth--- a/pallets/fungible/src/erc.rs
+++ b/pallets/fungible/src/erc.rs
@@ -24,12 +24,16 @@
weight,
};
use up_data_structs::CollectionMode;
-use pallet_common::erc::{CommonEvmHandler, PrecompileResult};
+use pallet_common::{
+ CollectionHandle,
+ erc::{CommonEvmHandler, PrecompileResult, CollectionCall},
+ eth::EthCrossAccount,
+};
use sp_std::vec::Vec;
use pallet_evm::{account::CrossAccountId, PrecompileHandle};
use pallet_evm_coder_substrate::{call, dispatch_to_evm};
use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};
-use pallet_common::{CollectionHandle, erc::CollectionCall};
+use sp_core::Get;
use crate::{
Allowance, Balance, Config, FungibleHandle, Pallet, SelfWeightOf, TotalSupply,
@@ -132,6 +136,11 @@
Ok(<Allowance<T>>::get((self.id, owner, spender)).into())
}
+
+ /// @notice Returns collection helper contract address
+ fn collection_helper_address(&self) -> Result<address> {
+ Ok(T::ContractAddress::get())
+ }
}
#[solidity_interface(name = ERC20Mintable)]
pallets/fungible/src/stubs/UniqueFungible.rawdiffbeforeafterbothbinary blob — no preview
pallets/fungible/src/stubs/UniqueFungible.soldiffbeforeafterboth--- a/pallets/fungible/src/stubs/UniqueFungible.sol
+++ b/pallets/fungible/src/stubs/UniqueFungible.sol
@@ -547,7 +547,7 @@
event Approval(address indexed owner, address indexed spender, uint256 value);
}
-/// @dev the ERC-165 identifier for this interface is 0x942e8b22
+/// @dev the ERC-165 identifier for this interface is 0x8cb847c4
contract ERC20 is Dummy, ERC165, ERC20Events {
/// @dev EVM selector for this function is: 0x06fdde03,
/// or in textual repr: name()
@@ -634,6 +634,15 @@
dummy;
return 0;
}
+
+ /// @notice Returns collection helper contract address
+ /// @dev EVM selector for this function is: 0x1896cce6,
+ /// or in textual repr: collectionHelperAddress()
+ function collectionHelperAddress() public view returns (address) {
+ require(false, stub_error);
+ dummy;
+ return 0x0000000000000000000000000000000000000000;
+ }
}
contract UniqueFungible is Dummy, ERC165, ERC20, ERC20Mintable, ERC20UniqueExtensions, Collection {}
pallets/nonfungible/src/erc.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -36,12 +36,14 @@
use pallet_evm_coder_substrate::dispatch_to_evm;
use sp_std::vec::Vec;
use pallet_common::{
+ CollectionHandle, CollectionPropertyPermissions, CommonCollectionOperations,
erc::{CommonEvmHandler, PrecompileResult, CollectionCall, static_property::key},
- CollectionHandle, CollectionPropertyPermissions, CommonCollectionOperations,
+ eth::EthCrossAccount,
};
use pallet_evm::{account::CrossAccountId, PrecompileHandle};
use pallet_evm_coder_substrate::call;
use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};
+use sp_core::Get;
use crate::{
AccountBalance, Config, CreateItemData, NonfungibleHandle, Pallet, TokenData, TokensMinted,
@@ -489,6 +491,11 @@
// TODO: Not implemetable
Err("not implemented".into())
}
+
+ /// @notice Returns collection helper contract address
+ fn collection_helper_address(&self) -> Result<address> {
+ Ok(T::ContractAddress::get())
+ }
}
/// @title ERC721 Token that can be irreversibly burned (destroyed).
pallets/nonfungible/src/stubs/UniqueNFT.rawdiffbeforeafterbothbinary blob — no preview
pallets/nonfungible/src/stubs/UniqueNFT.soldiffbeforeafterboth--- a/pallets/nonfungible/src/stubs/UniqueNFT.sol
+++ b/pallets/nonfungible/src/stubs/UniqueNFT.sol
@@ -920,7 +920,7 @@
/// @title ERC-721 Non-Fungible Token Standard
/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
-/// @dev the ERC-165 identifier for this interface is 0x80ac58cd
+/// @dev the ERC-165 identifier for this interface is 0x983a942b
contract ERC721 is Dummy, ERC165, ERC721Events {
/// @notice Count all NFTs assigned to an owner
/// @dev NFTs assigned to the zero address are considered invalid, and this
@@ -1050,6 +1050,15 @@
dummy;
return 0x0000000000000000000000000000000000000000;
}
+
+ /// @notice Returns collection helper contract address
+ /// @dev EVM selector for this function is: 0x1896cce6,
+ /// or in textual repr: collectionHelperAddress()
+ function collectionHelperAddress() public view returns (address) {
+ require(false, stub_error);
+ dummy;
+ return 0x0000000000000000000000000000000000000000;
+ }
}
contract UniqueNFT is
pallets/proxy-rmrk-core/src/lib.rsdiffbeforeafterboth--- a/pallets/proxy-rmrk-core/src/lib.rs
+++ b/pallets/proxy-rmrk-core/src/lib.rs
@@ -178,7 +178,7 @@
use RmrkProperty::*;
-/// Maximum number of levels of depth in the token nesting tree.
+/// A maximum number of levels of depth in the token nesting tree.
pub const NESTING_BUDGET: u32 = 5;
type PendingTarget = (CollectionId, TokenId);
pallets/refungible/src/erc.rsdiffbeforeafterboth--- a/pallets/refungible/src/erc.rs
+++ b/pallets/refungible/src/erc.rs
@@ -31,14 +31,14 @@
};
use frame_support::{BoundedBTreeMap, BoundedVec};
use pallet_common::{
- CollectionHandle, CollectionPropertyPermissions,
+ CollectionHandle, CollectionPropertyPermissions, CommonCollectionOperations,
erc::{CommonEvmHandler, CollectionCall, static_property::key},
- CommonCollectionOperations,
+ eth::EthCrossAccount,
};
use pallet_evm::{account::CrossAccountId, PrecompileHandle};
use pallet_evm_coder_substrate::{call, dispatch_to_evm};
use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};
-use sp_core::H160;
+use sp_core::{H160, Get};
use sp_std::{collections::btree_map::BTreeMap, vec::Vec, vec};
use up_data_structs::{
CollectionId, CollectionPropertiesVec, mapping::TokenAddressMapping, Property, PropertyKey,
@@ -482,6 +482,11 @@
// TODO: Not implemetable
Err("not implemented".into())
}
+
+ /// @notice Returns collection helper contract address
+ fn collection_helper_address(&self) -> Result<address> {
+ Ok(T::ContractAddress::get())
+ }
}
/// Returns amount of pieces of `token` that `owner` have
pallets/refungible/src/stubs/UniqueRefungible.rawdiffbeforeafterbothbinary blob — no preview
pallets/refungible/src/stubs/UniqueRefungible.soldiffbeforeafterboth--- a/pallets/refungible/src/stubs/UniqueRefungible.sol
+++ b/pallets/refungible/src/stubs/UniqueRefungible.sol
@@ -919,7 +919,7 @@
/// @title ERC-721 Non-Fungible Token Standard
/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
-/// @dev the ERC-165 identifier for this interface is 0x58800161
+/// @dev the ERC-165 identifier for this interface is 0x4016cd87
contract ERC721 is Dummy, ERC165, ERC721Events {
/// @notice Count all RFTs assigned to an owner
/// @dev RFTs assigned to the zero address are considered invalid, and this
@@ -1047,6 +1047,15 @@
dummy;
return 0x0000000000000000000000000000000000000000;
}
+
+ /// @notice Returns collection helper contract address
+ /// @dev EVM selector for this function is: 0x1896cce6,
+ /// or in textual repr: collectionHelperAddress()
+ function collectionHelperAddress() public view returns (address) {
+ require(false, stub_error);
+ dummy;
+ return 0x0000000000000000000000000000000000000000;
+ }
}
contract UniqueRefungible is
pallets/refungible/src/stubs/UniqueRefungibleToken.rawdiffbeforeafterbothbinary blob — no preview
pallets/unique/src/eth/mod.rsdiffbeforeafterboth--- a/pallets/unique/src/eth/mod.rs
+++ b/pallets/unique/src/eth/mod.rs
@@ -28,6 +28,7 @@
CollectionById,
dispatch::CollectionDispatch,
erc::{CollectionHelpersEvents, static_property::key},
+ eth::{map_eth_to_id, collection_id_to_address},
Pallet as PalletCommon,
};
use pallet_evm::{account::CrossAccountId, OnMethodCall, PrecompileHandle, PrecompileResult};
@@ -35,7 +36,7 @@
use sp_std::vec;
use up_data_structs::{
CollectionDescription, CollectionMode, CollectionName, CollectionTokenPrefix,
- CreateCollectionData,
+ CreateCollectionData, CollectionId,
};
use crate::{weights::WeightInfo, Config, SelfWeightOf};
@@ -361,6 +362,25 @@
.expect("Collection creation price should be convertible to u128");
Ok(price.into())
}
+
+ /// Returns address of a collection.
+ /// @param collectionId - CollectionId of the collection
+ /// @return eth mirror address of the collection
+ fn collection_address(&self, collection_id: uint32) -> Result<address> {
+ Ok(collection_id_to_address(collection_id.into()))
+ }
+
+ /// Returns collectionId of a collection.
+ /// @param collectionAddress - Eth address of the collection
+ /// @return collectionId of the collection
+ fn collection_id(&self, collection_address: address) -> Result<uint32> {
+ map_eth_to_id(&collection_address)
+ .map(|id| id.0)
+ .ok_or(Error::Revert(format!(
+ "failed to convert address {} into collectionId.",
+ collection_address
+ )))
+ }
}
/// Implements [`OnMethodCall`], which delegates call to [`EvmCollectionHelpers`]
pallets/unique/src/eth/stubs/CollectionHelpers.rawdiffbeforeafterbothbinary blob — no preview
pallets/unique/src/eth/stubs/CollectionHelpers.soldiffbeforeafterboth--- a/pallets/unique/src/eth/stubs/CollectionHelpers.sol
+++ b/pallets/unique/src/eth/stubs/CollectionHelpers.sol
@@ -24,7 +24,7 @@
}
/// @title Contract, which allows users to operate with collections
-/// @dev the ERC-165 identifier for this interface is 0x7dea03b1
+/// @dev the ERC-165 identifier for this interface is 0xe65011aa
contract CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents {
/// Create an NFT collection
/// @param name Name of the collection
@@ -130,4 +130,28 @@
dummy;
return 0;
}
+
+ /// Returns address of a collection.
+ /// @param collectionId - CollectionId of the collection
+ /// @return eth mirror address of the collection
+ /// @dev EVM selector for this function is: 0x2e716683,
+ /// or in textual repr: collectionAddress(uint32)
+ function collectionAddress(uint32 collectionId) public view returns (address) {
+ require(false, stub_error);
+ collectionId;
+ dummy;
+ return 0x0000000000000000000000000000000000000000;
+ }
+
+ /// Returns collectionId of a collection.
+ /// @param collectionAddress - Eth address of the collection
+ /// @return collectionId of the collection
+ /// @dev EVM selector for this function is: 0xb5cb7498,
+ /// or in textual repr: collectionId(address)
+ function collectionId(address collectionAddress) public view returns (uint32) {
+ require(false, stub_error);
+ collectionAddress;
+ dummy;
+ return 0;
+ }
}
pallets/unique/src/lib.rsdiffbeforeafterboth--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -86,6 +86,8 @@
use sp_std::{vec, vec::Vec};
use up_data_structs::{
MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH,
+ MAX_PROPERTIES_PER_ITEM, MAX_PROPERTY_KEY_LENGTH, MAX_PROPERTY_VALUE_LENGTH,
+ MAX_COLLECTION_PROPERTIES_SIZE, COLLECTION_ADMINS_LIMIT, MAX_TOKEN_PROPERTIES_SIZE,
CreateItemData, CollectionLimits, CollectionPermissions, CollectionId, CollectionMode, TokenId,
SponsorshipState, CreateCollectionData, CreateItemExData, budget, Property, PropertyKey,
PropertyKeyPermission,
@@ -102,7 +104,7 @@
pub mod weights;
use weights::WeightInfo;
-/// Maximum number of levels of depth in the token nesting tree.
+/// A maximum number of levels of depth in the token nesting tree.
pub const NESTING_BUDGET: u32 = 5;
decl_error! {
@@ -277,6 +279,46 @@
{
type Error = Error<T>;
+ #[doc = "A maximum number of levels of depth in the token nesting tree."]
+ const NESTING_BUDGET: u32 = NESTING_BUDGET;
+
+ #[doc = "Maximal length of a collection name."]
+ const MAX_COLLECTION_NAME_LENGTH: u32 = MAX_COLLECTION_NAME_LENGTH;
+
+ #[doc = "Maximal length of a collection description."]
+ const MAX_COLLECTION_DESCRIPTION_LENGTH: u32 = MAX_COLLECTION_DESCRIPTION_LENGTH;
+
+ #[doc = "Maximal length of a token prefix."]
+ const MAX_TOKEN_PREFIX_LENGTH: u32 = MAX_TOKEN_PREFIX_LENGTH;
+
+ #[doc = "Maximum admins per collection."]
+ const COLLECTION_ADMINS_LIMIT: u32 = COLLECTION_ADMINS_LIMIT;
+
+ #[doc = "Maximal length of a property key."]
+ const MAX_PROPERTY_KEY_LENGTH: u32 = MAX_PROPERTY_KEY_LENGTH;
+
+ #[doc = "Maximal length of a property value."]
+ const MAX_PROPERTY_VALUE_LENGTH: u32 = MAX_PROPERTY_VALUE_LENGTH;
+
+ #[doc = "A maximum number of token properties."]
+ const MAX_PROPERTIES_PER_ITEM: u32 = MAX_PROPERTIES_PER_ITEM;
+
+ #[doc = "Maximum size for all collection properties."]
+ const MAX_COLLECTION_PROPERTIES_SIZE: u32 = MAX_COLLECTION_PROPERTIES_SIZE;
+
+ #[doc = "Maximum size of all token properties."]
+ const MAX_TOKEN_PROPERTIES_SIZE: u32 = MAX_TOKEN_PROPERTIES_SIZE;
+
+ #[doc = "Default NFT collection limit."]
+ const NFT_DEFAULT_COLLECTION_LIMITS: CollectionLimits = CollectionLimits::with_default_limits(CollectionMode::NFT);
+
+ #[doc = "Default RFT collection limit."]
+ const RFT_DEFAULT_COLLECTION_LIMITS: CollectionLimits = CollectionLimits::with_default_limits(CollectionMode::ReFungible);
+
+ #[doc = "Default FT collection limit."]
+ const FT_DEFAULT_COLLECTION_LIMITS: CollectionLimits = CollectionLimits::with_default_limits(CollectionMode::Fungible(0));
+
+
pub fn deposit_event() = default;
fn on_initialize(_now: T::BlockNumber) -> Weight {
primitives/data-structs/CHANGELOG.mddiffbeforeafterboth--- a/primitives/data-structs/CHANGELOG.md
+++ b/primitives/data-structs/CHANGELOG.md
@@ -3,6 +3,7 @@
All notable changes to this project will be documented in this file.
<!-- bureaucrate goes here -->
+
## [v0.2.2] 2022-08-16
### Other changes
@@ -28,12 +29,19 @@
multiple users into `RefungibleMultipleItems` call.
## [v0.2.0] - 2022-08-01
+
### Deprecated
+
- `CreateReFungibleData::const_data`
## [v0.1.2] - 2022-07-25
+
### Added
+
- Type aliases `CollectionName`, `CollectionDescription`, `CollectionTokenPrefix`
+
## [v0.1.1] - 2022-07-22
+
### Added
-- Аields with properties to `CreateReFungibleData` and `CreateRefungibleExData`.
\ No newline at end of file
+
+- Fields with properties to `CreateReFungibleData` and `CreateRefungibleExData`.
primitives/data-structs/src/lib.rsdiffbeforeafterboth--- a/primitives/data-structs/src/lib.rs
+++ b/primitives/data-structs/src/lib.rs
@@ -120,22 +120,22 @@
// TODO: not used. Delete?
pub const COLLECTION_FIELD_LIMIT: u32 = CONST_ON_CHAIN_SCHEMA_LIMIT;
-/// Maximum length for collection name.
+/// Maximal length of a collection name.
pub const MAX_COLLECTION_NAME_LENGTH: u32 = 64;
-/// Maximum length for collection description.
+/// Maximal length of a collection description.
pub const MAX_COLLECTION_DESCRIPTION_LENGTH: u32 = 256;
-/// Maximal token prefix length.
+/// Maximal length of a token prefix.
pub const MAX_TOKEN_PREFIX_LENGTH: u32 = 16;
-/// Maximal lenght of property key.
+/// Maximal length of a property key.
pub const MAX_PROPERTY_KEY_LENGTH: u32 = 256;
-/// Maximal lenght of property value.
+/// Maximal length of a property value.
pub const MAX_PROPERTY_VALUE_LENGTH: u32 = 32768;
-/// Maximum properties that can be assigned to token.
+/// A maximum number of token properties.
pub const MAX_PROPERTIES_PER_ITEM: u32 = 64;
/// Maximal lenght of extended property value.
@@ -144,7 +144,7 @@
/// Maximum size for all collection properties.
pub const MAX_COLLECTION_PROPERTIES_SIZE: u32 = 40960;
-/// Maximum size for all token properties.
+/// Maximum size of all token properties.
pub const MAX_TOKEN_PROPERTIES_SIZE: u32 = 32768;
/// How much items can be created per single
@@ -609,6 +609,24 @@
}
impl CollectionLimits {
+ pub fn with_default_limits(collection_type: CollectionMode) -> Self {
+ CollectionLimits {
+ account_token_ownership_limit: Some(ACCOUNT_TOKEN_OWNERSHIP_LIMIT),
+ sponsored_data_size: Some(CUSTOM_DATA_LIMIT),
+ sponsored_data_rate_limit: Some(SponsoringRateLimit::SponsoringDisabled),
+ token_limit: Some(COLLECTION_TOKEN_LIMIT),
+ sponsor_transfer_timeout: match collection_type {
+ CollectionMode::NFT => Some(NFT_SPONSOR_TRANSFER_TIMEOUT),
+ CollectionMode::ReFungible => Some(REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),
+ CollectionMode::Fungible(_) => Some(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),
+ },
+ sponsor_approve_timeout: Some(SPONSOR_APPROVE_TIMEOUT),
+ owner_can_transfer: Some(false),
+ owner_can_destroy: Some(true),
+ transfers_enabled: Some(true),
+ }
+ }
+
/// Get effective value for [`account_token_ownership_limit`](self.account_token_ownership_limit).
pub fn account_token_ownership_limit(&self) -> u32 {
self.account_token_ownership_limit
tests/package.jsondiffbeforeafterboth--- a/tests/package.json
+++ b/tests/package.json
@@ -102,6 +102,7 @@
"testXcmTransferStatemine": "mocha --timeout 9999999 -r ts-node/register ./**/xcm/xcmTransferStatemine.test.ts statemineId=1000 uniqueId=5000",
"testXcmTransferMoonbeam": "mocha --timeout 9999999 -r ts-node/register ./**/xcm/xcmTransferMoonbeam.test.ts",
"benchMintingFee": "ts-node src/benchmarks/mintFee/benchmark.ts",
+ "testApiConsts": "mocha --timeout 9999999 -r ts-node/register ./**/apiConsts.test.ts",
"load": "mocha --timeout 9999999 -r ts-node/register './**/*.load.ts'",
"loadTransfer": "ts-node src/transfer.nload.ts",
"polkadot-types-fetch-metadata": "curl -H 'Content-Type: application/json' -d '{\"id\":\"1\", \"jsonrpc\":\"2.0\", \"method\": \"state_getMetadata\", \"params\":[]}' http://localhost:9933 > src/interfaces/metadata.json",
tests/src/apiConsts.test.tsdiffbeforeafterboth--- /dev/null
+++ b/tests/src/apiConsts.test.ts
@@ -0,0 +1,120 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+import {ApiPromise} from '@polkadot/api';
+import {ApiBase} from '@polkadot/api/base';
+import {usingPlaygrounds, itSub, expect} from './util';
+
+
+const MAX_COLLECTION_DESCRIPTION_LENGTH = 256n;
+const MAX_COLLECTION_NAME_LENGTH = 64n;
+const COLLECTION_ADMINS_LIMIT = 5n;
+const MAX_COLLECTION_PROPERTIES_SIZE = 40960n;
+const MAX_TOKEN_PREFIX_LENGTH = 16n;
+const MAX_PROPERTY_KEY_LENGTH = 256n;
+const MAX_PROPERTY_VALUE_LENGTH = 32768n;
+const MAX_PROPERTIES_PER_ITEM = 64n;
+const MAX_TOKEN_PROPERTIES_SIZE = 32768n;
+const NESTING_BUDGET = 5n;
+
+const DEFAULT_COLLETCTION_LIMIT = {
+ accountTokenOwnershipLimit: '1,000,000',
+ sponsoredDataSize: '2,048',
+ sponsoredDataRateLimit: 'SponsoringDisabled',
+ tokenLimit: '4,294,967,295',
+ sponsorTransferTimeout: '5',
+ sponsorApproveTimeout: '5',
+ ownerCanTransfer: false,
+ ownerCanDestroy: true,
+ transfersEnabled: true,
+};
+
+const EVM_COLLECTION_HELPERS_ADDRESS = '0x6c4e9fe1ae37a41e93cee429e8e1881abdcbb54f';
+const HELPERS_CONTRACT_ADDRESS = '0x842899ECF380553E8a4de75bF534cdf6fBF64049';
+
+describe('integration test: API UNIQUE consts', () => {
+ let api: ApiPromise;
+
+ before(async () => {
+ await usingPlaygrounds(async (helper) => {
+ api = await helper.getApi();
+ });
+ });
+
+ itSub('DEFAULT_NFT_COLLECTION_LIMITS', () => {
+ expect(api.consts.unique.nftDefaultCollectionLimits.toHuman()).to.deep.equal(DEFAULT_COLLETCTION_LIMIT);
+ });
+
+ itSub('DEFAULT_RFT_COLLECTION_LIMITS', () => {
+ expect(api.consts.unique.rftDefaultCollectionLimits.toHuman()).to.deep.equal(DEFAULT_COLLETCTION_LIMIT);
+ });
+
+ itSub('DEFAULT_FT_COLLECTION_LIMITS', () => {
+ expect(api.consts.unique.ftDefaultCollectionLimits.toHuman()).to.deep.equal(DEFAULT_COLLETCTION_LIMIT);
+ });
+
+ itSub('MAX_COLLECTION_NAME_LENGTH', () => {
+ checkConst(api.consts.unique.maxCollectionNameLength, MAX_COLLECTION_NAME_LENGTH);
+ });
+
+ itSub('MAX_COLLECTION_DESCRIPTION_LENGTH', () => {
+ checkConst(api.consts.unique.maxCollectionDescriptionLength, MAX_COLLECTION_DESCRIPTION_LENGTH);
+ });
+
+ itSub('MAX_COLLECTION_PROPERTIES_SIZE', () => {
+ checkConst(api.consts.unique.maxCollectionPropertiesSize, MAX_COLLECTION_PROPERTIES_SIZE);
+ });
+
+ itSub('MAX_TOKEN_PREFIX_LENGTH', () => {
+ checkConst(api.consts.unique.maxTokenPrefixLength, MAX_TOKEN_PREFIX_LENGTH);
+ });
+
+ itSub('MAX_PROPERTY_KEY_LENGTH', () => {
+ checkConst(api.consts.unique.maxPropertyKeyLength, MAX_PROPERTY_KEY_LENGTH);
+ });
+
+ itSub('MAX_PROPERTY_VALUE_LENGTH', () => {
+ checkConst(api.consts.unique.maxPropertyValueLength, MAX_PROPERTY_VALUE_LENGTH);
+ });
+
+ itSub('MAX_PROPERTIES_PER_ITEM', () => {
+ checkConst(api.consts.unique.maxPropertiesPerItem, MAX_PROPERTIES_PER_ITEM);
+ });
+
+ itSub('NESTING_BUDGET', () => {
+ checkConst(api.consts.unique.nestingBudget, NESTING_BUDGET);
+ });
+
+ itSub('MAX_TOKEN_PROPERTIES_SIZE', () => {
+ checkConst(api.consts.unique.maxTokenPropertiesSize, MAX_TOKEN_PROPERTIES_SIZE);
+ });
+
+ itSub('COLLECTION_ADMINS_LIMIT', () => {
+ checkConst(api.consts.unique.collectionAdminsLimit, COLLECTION_ADMINS_LIMIT);
+ });
+
+ itSub('HELPERS_CONTRACT_ADDRESS', () => {
+ expect(api.consts.evmContractHelpers.contractAddress.toString().toLowerCase()).to.be.equal(HELPERS_CONTRACT_ADDRESS.toLowerCase());
+ });
+
+ itSub('EVM_COLLECTION_HELPERS_ADDRESS', () => {
+ expect(api.consts.common.contractAddress.toString().toLowerCase()).to.be.equal(EVM_COLLECTION_HELPERS_ADDRESS.toLowerCase());
+ });
+});
+
+function checkConst<T>(constValue: any, expectedValue: T) {
+ expect(constValue.toBigInt()).equal(expectedValue);
+}
\ No newline at end of file
tests/src/eth/abi/collectionHelpers.jsondiffbeforeafterboth--- a/tests/src/eth/abi/collectionHelpers.json
+++ b/tests/src/eth/abi/collectionHelpers.json
@@ -32,6 +32,15 @@
"type": "event"
},
{
+ "inputs": [
+ { "internalType": "uint32", "name": "collectionId", "type": "uint32" }
+ ],
+ "name": "collectionAddress",
+ "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
"inputs": [],
"name": "collectionCreationFee",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
@@ -40,6 +49,19 @@
},
{
"inputs": [
+ {
+ "internalType": "address",
+ "name": "collectionAddress",
+ "type": "address"
+ }
+ ],
+ "name": "collectionId",
+ "outputs": [{ "internalType": "uint32", "name": "", "type": "uint32" }],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [
{ "internalType": "string", "name": "name", "type": "string" },
{ "internalType": "uint8", "name": "decimals", "type": "uint8" },
{ "internalType": "string", "name": "description", "type": "string" },
tests/src/eth/abi/fungible.jsondiffbeforeafterboth--- a/tests/src/eth/abi/fungible.json
+++ b/tests/src/eth/abi/fungible.json
@@ -201,6 +201,13 @@
},
{
"inputs": [],
+ "name": "collectionHelperAddress",
+ "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
"name": "collectionOwner",
"outputs": [
{
tests/src/eth/abi/nonFungible.jsondiffbeforeafterboth--- a/tests/src/eth/abi/nonFungible.json
+++ b/tests/src/eth/abi/nonFungible.json
@@ -231,6 +231,13 @@
},
{
"inputs": [],
+ "name": "collectionHelperAddress",
+ "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
"name": "collectionOwner",
"outputs": [
{
tests/src/eth/abi/reFungible.jsondiffbeforeafterboth--- a/tests/src/eth/abi/reFungible.json
+++ b/tests/src/eth/abi/reFungible.json
@@ -213,6 +213,13 @@
},
{
"inputs": [],
+ "name": "collectionHelperAddress",
+ "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
"name": "collectionOwner",
"outputs": [
{
tests/src/eth/api/CollectionHelpers.soldiffbeforeafterboth--- a/tests/src/eth/api/CollectionHelpers.sol
+++ b/tests/src/eth/api/CollectionHelpers.sol
@@ -19,7 +19,7 @@
}
/// @title Contract, which allows users to operate with collections
-/// @dev the ERC-165 identifier for this interface is 0x7dea03b1
+/// @dev the ERC-165 identifier for this interface is 0xe65011aa
interface CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents {
/// Create an NFT collection
/// @param name Name of the collection
@@ -78,4 +78,18 @@
/// @dev EVM selector for this function is: 0xd23a7ab1,
/// or in textual repr: collectionCreationFee()
function collectionCreationFee() external view returns (uint256);
+
+ /// Returns address of a collection.
+ /// @param collectionId - CollectionId of the collection
+ /// @return eth mirror address of the collection
+ /// @dev EVM selector for this function is: 0x2e716683,
+ /// or in textual repr: collectionAddress(uint32)
+ function collectionAddress(uint32 collectionId) external view returns (address);
+
+ /// Returns collectionId of a collection.
+ /// @param collectionAddress - Eth address of the collection
+ /// @return collectionId of the collection
+ /// @dev EVM selector for this function is: 0xb5cb7498,
+ /// or in textual repr: collectionId(address)
+ function collectionId(address collectionAddress) external view returns (uint32);
}
tests/src/eth/api/UniqueFungible.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueFungible.sol
+++ b/tests/src/eth/api/UniqueFungible.sol
@@ -354,7 +354,7 @@
event Approval(address indexed owner, address indexed spender, uint256 value);
}
-/// @dev the ERC-165 identifier for this interface is 0x942e8b22
+/// @dev the ERC-165 identifier for this interface is 0x8cb847c4
interface ERC20 is Dummy, ERC165, ERC20Events {
/// @dev EVM selector for this function is: 0x06fdde03,
/// or in textual repr: name()
@@ -395,6 +395,11 @@
/// @dev EVM selector for this function is: 0xdd62ed3e,
/// or in textual repr: allowance(address,address)
function allowance(address owner, address spender) external view returns (uint256);
+
+ /// @notice Returns collection helper contract address
+ /// @dev EVM selector for this function is: 0x1896cce6,
+ /// or in textual repr: collectionHelperAddress()
+ function collectionHelperAddress() external view returns (address);
}
interface UniqueFungible is Dummy, ERC165, ERC20, ERC20Mintable, ERC20UniqueExtensions, Collection {}
tests/src/eth/api/UniqueNFT.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueNFT.sol
+++ b/tests/src/eth/api/UniqueNFT.sol
@@ -605,7 +605,7 @@
/// @title ERC-721 Non-Fungible Token Standard
/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
-/// @dev the ERC-165 identifier for this interface is 0x80ac58cd
+/// @dev the ERC-165 identifier for this interface is 0x983a942b
interface ERC721 is Dummy, ERC165, ERC721Events {
/// @notice Count all NFTs assigned to an owner
/// @dev NFTs assigned to the zero address are considered invalid, and this
@@ -685,6 +685,11 @@
/// @dev EVM selector for this function is: 0xe985e9c5,
/// or in textual repr: isApprovedForAll(address,address)
function isApprovedForAll(address owner, address operator) external view returns (address);
+
+ /// @notice Returns collection helper contract address
+ /// @dev EVM selector for this function is: 0x1896cce6,
+ /// or in textual repr: collectionHelperAddress()
+ function collectionHelperAddress() external view returns (address);
}
interface UniqueNFT is
tests/src/eth/api/UniqueRefungible.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueRefungible.sol
+++ b/tests/src/eth/api/UniqueRefungible.sol
@@ -604,7 +604,7 @@
/// @title ERC-721 Non-Fungible Token Standard
/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
-/// @dev the ERC-165 identifier for this interface is 0x58800161
+/// @dev the ERC-165 identifier for this interface is 0x4016cd87
interface ERC721 is Dummy, ERC165, ERC721Events {
/// @notice Count all RFTs assigned to an owner
/// @dev RFTs assigned to the zero address are considered invalid, and this
@@ -682,6 +682,11 @@
/// @dev EVM selector for this function is: 0xe985e9c5,
/// or in textual repr: isApprovedForAll(address,address)
function isApprovedForAll(address owner, address operator) external view returns (address);
+
+ /// @notice Returns collection helper contract address
+ /// @dev EVM selector for this function is: 0x1896cce6,
+ /// or in textual repr: collectionHelperAddress()
+ function collectionHelperAddress() external view returns (address);
}
interface UniqueRefungible is
tests/src/eth/collectionHelperAddress.test.tsdiffbeforeafterboth--- /dev/null
+++ b/tests/src/eth/collectionHelperAddress.test.ts
@@ -0,0 +1,72 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+import {itEth, usingEthPlaygrounds, expect} from './util';
+import {IKeyringPair} from '@polkadot/types/types';
+import {Pallets} from '../util';
+
+const EVM_COLLECTION_HELPERS_ADDRESS = '0x6c4e9fe1ae37a41e93cee429e8e1881abdcbb54f';
+
+describe('[eth]CollectionHelperAddress test: ERC20/ERC721 ', () => {
+ let donor: IKeyringPair;
+
+ before(async function() {
+ await usingEthPlaygrounds(async (helper, privateKey) => {
+ donor = await privateKey({filename: __filename});
+ });
+ });
+
+ itEth('NFT', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+
+ const {collectionAddress: nftCollectionAddress} = await helper.eth.createNFTCollection(owner, 'Sponsor', 'absolutely anything', 'ROC');
+ const nftCollection = helper.ethNativeContract.collection(nftCollectionAddress, 'nft', owner);
+
+ expect((await nftCollection.methods.collectionHelperAddress().call())
+ .toString().toLowerCase()).to.be.equal(EVM_COLLECTION_HELPERS_ADDRESS);
+ });
+
+ itEth.ifWithPallets('RFT ', [Pallets.ReFungible], async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+
+ const {collectionAddress: rftCollectionAddress} = await helper.eth.createRFTCollection(owner, 'Sponsor', 'absolutely anything', 'ROC');
+
+ const rftCollection = helper.ethNativeContract.collection(rftCollectionAddress, 'rft', owner);
+ expect((await rftCollection.methods.collectionHelperAddress().call())
+ .toString().toLowerCase()).to.be.equal(EVM_COLLECTION_HELPERS_ADDRESS);
+ });
+
+ itEth('FT', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+
+ const {collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Sponsor', 18, 'absolutely anything', 'ROC');
+ const collection = helper.ethNativeContract.collection(collectionAddress, 'ft', owner);
+
+ expect((await collection.methods.collectionHelperAddress().call())
+ .toString().toLowerCase()).to.be.equal(EVM_COLLECTION_HELPERS_ADDRESS);
+ });
+
+ itEth('[collectionHelpers] convert collectionId into address', async ({helper}) => {
+ const owner = await helper.eth.createAccountWithBalance(donor);
+ const collectionId = 7;
+ const collectionAddress = helper.ethAddress.fromCollectionId(collectionId);
+ const helperContract = helper.ethNativeContract.collectionHelpers(owner);
+
+ expect(await helperContract.methods.collectionAddress(collectionId).call()).to.be.equal(collectionAddress);
+ expect(parseInt(await helperContract.methods.collectionId(collectionAddress).call())).to.be.equal(collectionId);
+ });
+
+});
tests/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
@@ -103,12 +103,12 @@
contractHelpers(caller: string): Contract {
const web3 = this.helper.getWeb3();
- return new web3.eth.Contract(contractHelpersAbi as any, '0x842899ECF380553E8a4de75bF534cdf6fBF64049', {from: caller, gas: this.helper.eth.DEFAULT_GAS});
+ return new web3.eth.Contract(contractHelpersAbi as any, this.helper.getApi().consts.evmContractHelpers.contractAddress.toString(), {from: caller, gas: this.helper.eth.DEFAULT_GAS});
}
collectionHelpers(caller: string) {
const web3 = this.helper.getWeb3();
- return new web3.eth.Contract(collectionHelpersAbi as any, '0x6c4e9fe1ae37a41e93cee429e8e1881abdcbb54f', {from: caller, gas: this.helper.eth.DEFAULT_GAS});
+ return new web3.eth.Contract(collectionHelpersAbi as any, this.helper.getApi().consts.common.contractAddress.toString(), {from: caller, gas: this.helper.eth.DEFAULT_GAS});
}
collection(address: string, mode: TCollectionMode, caller?: string, mergeDeprecated = false): Contract {
tests/src/interfaces/augment-api-consts.tsdiffbeforeafterboth--- a/tests/src/interfaces/augment-api-consts.ts
+++ b/tests/src/interfaces/augment-api-consts.ts
@@ -8,8 +8,8 @@
import type { ApiTypes, AugmentedConst } from '@polkadot/api-base/types';
import type { Option, u128, u16, u32, u64, u8 } from '@polkadot/types-codec';
import type { Codec } from '@polkadot/types-codec/types';
-import type { Perbill, Permill, Weight } from '@polkadot/types/interfaces/runtime';
-import type { FrameSupportPalletId, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, SpVersionRuntimeVersion, SpWeightsRuntimeDbWeight, XcmV1MultiLocation } from '@polkadot/types/lookup';
+import type { H160, Perbill, Permill, Weight } from '@polkadot/types/interfaces/runtime';
+import type { FrameSupportPalletId, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, SpVersionRuntimeVersion, SpWeightsRuntimeDbWeight, UpDataStructsCollectionLimits, XcmV1MultiLocation } from '@polkadot/types/lookup';
export type __AugmentedConst<ApiType extends ApiTypes> = AugmentedConst<ApiType>;
@@ -70,6 +70,10 @@
**/
collectionCreationPrice: u128 & AugmentedConst<ApiType>;
/**
+ * Address under which the CollectionHelper contract would be available.
+ **/
+ contractAddress: H160 & AugmentedConst<ApiType>;
+ /**
* Generic const
**/
[key: string]: Codec;
@@ -82,6 +86,16 @@
**/
[key: string]: Codec;
};
+ evmContractHelpers: {
+ /**
+ * Address, under which magic contract will be available
+ **/
+ contractAddress: H160 & AugmentedConst<ApiType>;
+ /**
+ * Generic const
+ **/
+ [key: string]: Codec;
+ };
inflation: {
/**
* Number of blocks that pass between treasury balance updates due to inflation
@@ -231,6 +245,64 @@
**/
[key: string]: Codec;
};
+ unique: {
+ /**
+ * Maximum admins per collection.
+ **/
+ collectionAdminsLimit: u32 & AugmentedConst<ApiType>;
+ /**
+ * Default FT collection limit.
+ **/
+ ftDefaultCollectionLimits: UpDataStructsCollectionLimits & AugmentedConst<ApiType>;
+ /**
+ * Maximal length of a collection description.
+ **/
+ maxCollectionDescriptionLength: u32 & AugmentedConst<ApiType>;
+ /**
+ * Maximal length of a collection name.
+ **/
+ maxCollectionNameLength: u32 & AugmentedConst<ApiType>;
+ /**
+ * Maximum size for all collection properties.
+ **/
+ maxCollectionPropertiesSize: u32 & AugmentedConst<ApiType>;
+ /**
+ * A maximum number of token properties.
+ **/
+ maxPropertiesPerItem: u32 & AugmentedConst<ApiType>;
+ /**
+ * Maximal length of a property key.
+ **/
+ maxPropertyKeyLength: u32 & AugmentedConst<ApiType>;
+ /**
+ * Maximal length of a property value.
+ **/
+ maxPropertyValueLength: u32 & AugmentedConst<ApiType>;
+ /**
+ * Maximal length of a token prefix.
+ **/
+ maxTokenPrefixLength: u32 & AugmentedConst<ApiType>;
+ /**
+ * Maximum size of all token properties.
+ **/
+ maxTokenPropertiesSize: u32 & AugmentedConst<ApiType>;
+ /**
+ * A maximum number of levels of depth in the token nesting tree.
+ **/
+ nestingBudget: u32 & AugmentedConst<ApiType>;
+ /**
+ * Default NFT collection limit.
+ **/
+ nftDefaultCollectionLimits: UpDataStructsCollectionLimits & AugmentedConst<ApiType>;
+ /**
+ * Default RFT collection limit.
+ **/
+ rftDefaultCollectionLimits: UpDataStructsCollectionLimits & AugmentedConst<ApiType>;
+ /**
+ * Generic const
+ **/
+ [key: string]: Codec;
+ };
vesting: {
/**
* The minimum amount transferred to call `vested_transfer`.