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.rsdiffbeforeafterboth--- a/pallets/evm-contract-helpers/src/lib.rs
+++ b/pallets/evm-contract-helpers/src/lib.rs
@@ -47,6 +47,7 @@
type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;
/// Address, under which magic contract will be available
+ #[pallet::constant]
type ContractAddress: Get<H160>;
/// 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.rsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! # Primitives crate.18//!19//! This crate contains types, traits and constants.2021#![cfg_attr(not(feature = "std"), no_std)]2223use core::{24 convert::{TryFrom, TryInto},25 fmt,26};27use frame_support::{28 storage::{bounded_btree_map::BoundedBTreeMap, bounded_btree_set::BoundedBTreeSet},29 traits::Get,30 parameter_types,31};3233#[cfg(feature = "serde")]34use serde::{Serialize, Deserialize};3536use sp_core::U256;37use sp_runtime::{ArithmeticError, sp_std::prelude::Vec, Permill};38use codec::{Decode, Encode, EncodeLike, MaxEncodedLen};39use bondrewd::Bitfields;40use frame_support::{BoundedVec, traits::ConstU32};41use derivative::Derivative;42use scale_info::TypeInfo;4344// RMRK45use rmrk_traits::{46 CollectionInfo, NftInfo, ResourceInfo, PropertyInfo, BaseInfo, PartType, Theme, ThemeProperty,47 ResourceTypes, BasicResource, ComposableResource, SlotResource, EquippableList,48};49pub use rmrk_traits::{50 primitives::{51 CollectionId as RmrkCollectionId, NftId as RmrkNftId, BaseId as RmrkBaseId,52 SlotId as RmrkSlotId, PartId as RmrkPartId, ResourceId as RmrkResourceId,53 },54 NftChild as RmrkNftChild, AccountIdOrCollectionNftTuple as RmrkAccountIdOrCollectionNftTuple,55 FixedPart as RmrkFixedPart, SlotPart as RmrkSlotPart,56};5758mod bondrewd_codec;59mod bounded;60pub mod budget;61pub mod mapping;62mod migration;6364/// Maximum of decimal points.65pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;6667/// Maximum pieces for refungible token.68pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;69pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;7071/// Maximum tokens for user.72pub const MAX_TOKEN_OWNERSHIP: u32 = if cfg!(not(feature = "limit-testing")) {73 100_00074} else {75 1076};7778/// Maximum for collections can be created.79pub const COLLECTION_NUMBER_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {80 100_00081} else {82 1083};8485/// Maximum for various custom data of token.86pub const CUSTOM_DATA_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {87 204888} else {89 1090};9192/// Maximum admins per collection.93pub const COLLECTION_ADMINS_LIMIT: u32 = 5;9495/// Maximum tokens per collection.96pub const COLLECTION_TOKEN_LIMIT: u32 = u32::MAX;9798/// Maximum tokens per account.99pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) {100 1_000_000101} else {102 10103};104105/// Default timeout for transfer sponsoring NFT item.106pub const NFT_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;107/// Default timeout for transfer sponsoring fungible item.108pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;109/// Default timeout for transfer sponsoring refungible item.110pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;111112/// Default timeout for sponsored approving.113pub const SPONSOR_APPROVE_TIMEOUT: u32 = 5;114115// Schema limits116pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 8192;117pub const VARIABLE_ON_CHAIN_SCHEMA_LIMIT: u32 = 8192;118pub const CONST_ON_CHAIN_SCHEMA_LIMIT: u32 = 32768;119120// TODO: not used. Delete?121pub const COLLECTION_FIELD_LIMIT: u32 = CONST_ON_CHAIN_SCHEMA_LIMIT;122123/// Maximum length for collection name.124pub const MAX_COLLECTION_NAME_LENGTH: u32 = 64;125126/// Maximum length for collection description.127pub const MAX_COLLECTION_DESCRIPTION_LENGTH: u32 = 256;128129/// Maximal token prefix length.130pub const MAX_TOKEN_PREFIX_LENGTH: u32 = 16;131132/// Maximal lenght of property key.133pub const MAX_PROPERTY_KEY_LENGTH: u32 = 256;134135/// Maximal lenght of property value.136pub const MAX_PROPERTY_VALUE_LENGTH: u32 = 32768;137138/// Maximum properties that can be assigned to token.139pub const MAX_PROPERTIES_PER_ITEM: u32 = 64;140141/// Maximal lenght of extended property value.142pub const MAX_AUX_PROPERTY_VALUE_LENGTH: u32 = 2048;143144/// Maximum size for all collection properties.145pub const MAX_COLLECTION_PROPERTIES_SIZE: u32 = 40960;146147/// Maximum size for all token properties.148pub const MAX_TOKEN_PROPERTIES_SIZE: u32 = 32768;149150/// How much items can be created per single151/// create_many call.152pub const MAX_ITEMS_PER_BATCH: u32 = 200;153154/// Used for limit bounded types of token custom data.155pub type CustomDataLimit = ConstU32<CUSTOM_DATA_LIMIT>;156157/// Collection id.158#[derive(159 Encode,160 Decode,161 PartialEq,162 Eq,163 PartialOrd,164 Ord,165 Clone,166 Copy,167 Debug,168 Default,169 TypeInfo,170 MaxEncodedLen,171)]172#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]173pub struct CollectionId(pub u32);174impl EncodeLike<u32> for CollectionId {}175impl EncodeLike<CollectionId> for u32 {}176177/// Token id.178#[derive(179 Encode,180 Decode,181 PartialEq,182 Eq,183 PartialOrd,184 Ord,185 Clone,186 Copy,187 Debug,188 Default,189 TypeInfo,190 MaxEncodedLen,191)]192#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]193pub struct TokenId(pub u32);194impl EncodeLike<u32> for TokenId {}195impl EncodeLike<TokenId> for u32 {}196197impl TokenId {198 /// Try to get next token id.199 ///200 /// If next id cause overflow, then [`ArithmeticError::Overflow`] returned.201 pub fn try_next(self) -> Result<TokenId, ArithmeticError> {202 self.0203 .checked_add(1)204 .ok_or(ArithmeticError::Overflow)205 .map(Self)206 }207}208209impl From<TokenId> for U256 {210 fn from(t: TokenId) -> Self {211 t.0.into()212 }213}214215impl TryFrom<U256> for TokenId {216 type Error = &'static str;217218 fn try_from(value: U256) -> Result<Self, Self::Error> {219 Ok(TokenId(value.try_into().map_err(|_| "too large token id")?))220 }221}222223/// Token data.224#[struct_versioning::versioned(version = 2, upper)]225#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]226#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]227pub struct TokenData<CrossAccountId> {228 /// Properties of token.229 pub properties: Vec<Property>,230231 /// Token owner.232 pub owner: Option<CrossAccountId>,233234 /// Token pieces.235 #[version(2.., upper(0))]236 pub pieces: u128,237}238239// TODO: unused type240pub struct OverflowError;241impl From<OverflowError> for &'static str {242 fn from(_: OverflowError) -> Self {243 "overflow occured"244 }245}246247/// Alias for decimal points type.248pub type DecimalPoints = u8;249250/// Collection mode.251///252/// Collection can represent various types of tokens.253/// Each collection can contain only one type of tokens at a time.254/// This type helps to understand which tokens the collection contains.255#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]256#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]257pub enum CollectionMode {258 /// Non fungible tokens.259 NFT,260 /// Fungible tokens.261 Fungible(DecimalPoints),262 /// Refungible tokens.263 ReFungible,264}265266impl CollectionMode {267 /// Get collection mod as number.268 pub fn id(&self) -> u8 {269 match self {270 CollectionMode::NFT => 1,271 CollectionMode::Fungible(_) => 2,272 CollectionMode::ReFungible => 3,273 }274 }275}276277// TODO: unused trait278pub trait SponsoringResolve<AccountId, Call> {279 fn resolve(who: &AccountId, call: &Call) -> Option<AccountId>;280}281282/// Access mode for some token operations.283#[derive(Encode, Decode, Eq, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)]284#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]285pub enum AccessMode {286 /// Access grant for owner and admins. Used as default.287 Normal,288 /// Like a [`Normal`](AccessMode::Normal) but also users in allow list.289 AllowList,290}291impl Default for AccessMode {292 fn default() -> Self {293 Self::Normal294 }295}296297// TODO: remove in future.298#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]299#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]300pub enum SchemaVersion {301 ImageURL,302 Unique,303}304impl Default for SchemaVersion {305 fn default() -> Self {306 Self::ImageURL307 }308}309310// TODO: unused type311#[derive(Encode, Decode, Default, Debug, Clone, PartialEq, TypeInfo)]312#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]313pub struct Ownership<AccountId> {314 pub owner: AccountId,315 pub fraction: u128,316}317318/// The state of collection sponsorship.319#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]320#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]321pub enum SponsorshipState<AccountId> {322 /// The fees are applied to the transaction sender.323 Disabled,324 /// The sponsor is under consideration. Until the sponsor gives his consent,325 /// the fee will still be charged to sender.326 Unconfirmed(AccountId),327 /// Transactions are sponsored by specified account.328 Confirmed(AccountId),329}330331impl<AccountId> SponsorshipState<AccountId> {332 /// Get a sponsor of the collection who has confirmed his status.333 pub fn sponsor(&self) -> Option<&AccountId> {334 match self {335 Self::Confirmed(sponsor) => Some(sponsor),336 _ => None,337 }338 }339340 /// Get a sponsor of the collection who has pending or confirmed status.341 pub fn pending_sponsor(&self) -> Option<&AccountId> {342 match self {343 Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),344 _ => None,345 }346 }347348 /// Whether the sponsorship is confirmed.349 pub fn confirmed(&self) -> bool {350 matches!(self, Self::Confirmed(_))351 }352}353354impl<T> Default for SponsorshipState<T> {355 fn default() -> Self {356 Self::Disabled357 }358}359360pub type CollectionName = BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>;361pub type CollectionDescription = BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>;362pub type CollectionTokenPrefix = BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>;363364#[derive(Bitfields, Clone, Copy, PartialEq, Eq, Debug, Default)]365#[bondrewd(enforce_bytes = 1)]366pub struct CollectionFlags {367 /// Tokens in foreign collections can be transferred, but not burnt368 #[bondrewd(bits = "0..1")]369 pub foreign: bool,370 /// Supports ERC721Metadata371 #[bondrewd(bits = "1..2")]372 pub erc721metadata: bool,373 /// External collections can't be managed using `unique` api374 #[bondrewd(bits = "7..8")]375 pub external: bool,376377 #[bondrewd(reserve, bits = "2..7")]378 pub reserved: u8,379}380bondrewd_codec!(CollectionFlags);381382/// Base structure for represent collection.383///384/// Used to provide basic functionality for all types of collections.385///386/// #### Note387/// Collection parameters, used in storage (see [`RpcCollection`] for the RPC version).388#[struct_versioning::versioned(version = 2, upper)]389#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen)]390pub struct Collection<AccountId> {391 /// Collection owner account.392 pub owner: AccountId,393394 /// Collection mode.395 pub mode: CollectionMode,396397 /// Access mode.398 #[version(..2)]399 pub access: AccessMode,400401 /// Collection name.402 pub name: CollectionName,403404 /// Collection description.405 pub description: CollectionDescription,406407 /// Token prefix.408 pub token_prefix: CollectionTokenPrefix,409410 #[version(..2)]411 pub mint_mode: bool,412413 #[version(..2)]414 pub offchain_schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,415416 #[version(..2)]417 pub schema_version: SchemaVersion,418419 /// The state of sponsorship of the collection.420 pub sponsorship: SponsorshipState<AccountId>,421422 /// Collection limits.423 pub limits: CollectionLimits,424425 /// Collection permissions.426 #[version(2.., upper(Default::default()))]427 pub permissions: CollectionPermissions,428429 #[version(2.., upper(Default::default()))]430 pub flags: CollectionFlags,431432 #[version(..2)]433 pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,434435 #[version(..2)]436 pub const_on_chain_schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>,437438 #[version(..2)]439 pub meta_update_permission: MetaUpdatePermission,440}441442#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]443#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]444pub struct RpcCollectionFlags {445 /// Is collection is foreign.446 pub foreign: bool,447 /// Collection supports ERC721Metadata.448 pub erc721metadata: bool,449}450451/// Collection parameters, used in RPC calls (see [`Collection`] for the storage version).452#[struct_versioning::versioned(version = 2, upper)]453#[derive(Encode, Decode, Clone, PartialEq, TypeInfo)]454#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]455pub struct RpcCollection<AccountId> {456 /// Collection owner account.457 pub owner: AccountId,458459 /// Collection mode.460 pub mode: CollectionMode,461462 /// Collection name.463 pub name: Vec<u16>,464465 /// Collection description.466 pub description: Vec<u16>,467468 /// Token prefix.469 pub token_prefix: Vec<u8>,470471 /// The state of sponsorship of the collection.472 pub sponsorship: SponsorshipState<AccountId>,473474 /// Collection limits.475 pub limits: CollectionLimits,476477 /// Collection permissions.478 pub permissions: CollectionPermissions,479480 /// Token property permissions.481 pub token_property_permissions: Vec<PropertyKeyPermission>,482483 /// Collection properties.484 pub properties: Vec<Property>,485486 /// Is collection read only.487 pub read_only: bool,488489 /// Extra collection flags490 #[version(2.., upper(RpcCollectionFlags {foreign: false, erc721metadata: false}))]491 pub flags: RpcCollectionFlags,492}493494/// Data used for create collection.495///496/// All fields are wrapped in [`Option`], where `None` means chain default.497#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Derivative, MaxEncodedLen)]498#[derivative(Debug, Default(bound = ""))]499pub struct CreateCollectionData<AccountId> {500 /// Collection mode.501 #[derivative(Default(value = "CollectionMode::NFT"))]502 pub mode: CollectionMode,503504 /// Access mode.505 pub access: Option<AccessMode>,506507 /// Collection name.508 pub name: CollectionName,509510 /// Collection description.511 pub description: CollectionDescription,512513 /// Token prefix.514 pub token_prefix: CollectionTokenPrefix,515516 /// Pending collection sponsor.517 pub pending_sponsor: Option<AccountId>,518519 /// Collection limits.520 pub limits: Option<CollectionLimits>,521522 /// Collection permissions.523 pub permissions: Option<CollectionPermissions>,524525 /// Token property permissions.526 pub token_property_permissions: CollectionPropertiesPermissionsVec,527528 /// Collection properties.529 pub properties: CollectionPropertiesVec,530}531532/// Bounded vector of properties permissions. Max length is [`MAX_PROPERTIES_PER_ITEM`].533// TODO: maybe rename to PropertiesPermissionsVec534pub type CollectionPropertiesPermissionsVec =535 BoundedVec<PropertyKeyPermission, ConstU32<MAX_PROPERTIES_PER_ITEM>>;536537/// Bounded vector of properties. Max length is [`MAX_PROPERTIES_PER_ITEM`].538pub type CollectionPropertiesVec = BoundedVec<Property, ConstU32<MAX_PROPERTIES_PER_ITEM>>;539540/// Limits and restrictions of a collection.541///542/// All fields are wrapped in [`Option`], where `None` means chain default.543///544/// Update with `pallet_common::Pallet::clamp_limits`.545// IMPORTANT: When adding/removing fields from this struct - don't forget to also546#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]547#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]548// When adding/removing fields from this struct - don't forget to also update with `pallet_common::Pallet::clamp_limits`.549// TODO: move `pallet_common::Pallet::clamp_limits` into `impl CollectionLimits`.550// TODO: may be remove [`Option`] and **pub** from fields and create struct with default values.551pub struct CollectionLimits {552 /// How many tokens can a user have on one account.553 /// * Default - [`ACCOUNT_TOKEN_OWNERSHIP_LIMIT`].554 /// * Limit - [`MAX_TOKEN_OWNERSHIP`].555 pub account_token_ownership_limit: Option<u32>,556557 /// How many bytes of data are available for sponsorship.558 /// * Default - [`CUSTOM_DATA_LIMIT`].559 /// * Limit - [`CUSTOM_DATA_LIMIT`].560 pub sponsored_data_size: Option<u32>,561562 // FIXME should we delete this or repurpose it?563 /// Times in how many blocks we sponsor data.564 ///565 /// If is `Some(v)` then **setVariableMetadata** is sponsored if there is `v` block between transactions.566 ///567 /// * Default - [`SponsoringDisabled`](SponsoringRateLimit::SponsoringDisabled).568 /// * Limit - [`MAX_SPONSOR_TIMEOUT`].569 ///570 /// In any case, chain default: [`SponsoringRateLimit::SponsoringDisabled`]571 pub sponsored_data_rate_limit: Option<SponsoringRateLimit>,572 /// Maximum amount of tokens inside the collection. Chain default: [`COLLECTION_TOKEN_LIMIT`]573574 /// How many tokens can be mined into this collection.575 ///576 /// * Default - [`COLLECTION_TOKEN_LIMIT`].577 /// * Limit - [`COLLECTION_TOKEN_LIMIT`].578 pub token_limit: Option<u32>,579580 /// Timeouts for transfer sponsoring.581 ///582 /// * Default583 /// - **Fungible** - [`FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`]584 /// - **NFT** - [`NFT_SPONSOR_TRANSFER_TIMEOUT`]585 /// - **Refungible** - [`REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT`]586 /// * Limit - [`MAX_SPONSOR_TIMEOUT`].587 pub sponsor_transfer_timeout: Option<u32>,588589 /// Timeout for sponsoring an approval in passed blocks.590 ///591 /// * Default - [`SPONSOR_APPROVE_TIMEOUT`].592 /// * Limit - [`MAX_SPONSOR_TIMEOUT`].593 pub sponsor_approve_timeout: Option<u32>,594595 /// Whether the collection owner of the collection can send tokens (which belong to other users).596 ///597 /// * Default - **false**.598 pub owner_can_transfer: Option<bool>,599600 /// Can the collection owner burn other people's tokens.601 ///602 /// * Default - **true**.603 pub owner_can_destroy: Option<bool>,604605 /// Is it possible to send tokens from this collection between users.606 ///607 /// * Default - **true**.608 pub transfers_enabled: Option<bool>,609}610611impl CollectionLimits {612 /// Get effective value for [`account_token_ownership_limit`](self.account_token_ownership_limit).613 pub fn account_token_ownership_limit(&self) -> u32 {614 self.account_token_ownership_limit615 .unwrap_or(ACCOUNT_TOKEN_OWNERSHIP_LIMIT)616 .min(MAX_TOKEN_OWNERSHIP)617 }618619 /// Get effective value for [`sponsored_data_size`](self.sponsored_data_size).620 pub fn sponsored_data_size(&self) -> u32 {621 self.sponsored_data_size622 .unwrap_or(CUSTOM_DATA_LIMIT)623 .min(CUSTOM_DATA_LIMIT)624 }625626 /// Get effective value for [`token_limit`](self.token_limit).627 pub fn token_limit(&self) -> u32 {628 self.token_limit629 .unwrap_or(COLLECTION_TOKEN_LIMIT)630 .min(COLLECTION_TOKEN_LIMIT)631 }632633 // TODO: may be replace u32 to mode?634 /// Get effective value for [`sponsor_transfer_timeout`](self.sponsor_transfer_timeout).635 pub fn sponsor_transfer_timeout(&self, default: u32) -> u32 {636 self.sponsor_transfer_timeout637 .unwrap_or(default)638 .min(MAX_SPONSOR_TIMEOUT)639 }640641 /// Get effective value for [`sponsor_approve_timeout`](self.sponsor_approve_timeout).642 pub fn sponsor_approve_timeout(&self) -> u32 {643 self.sponsor_approve_timeout644 .unwrap_or(SPONSOR_APPROVE_TIMEOUT)645 .min(MAX_SPONSOR_TIMEOUT)646 }647648 /// Get effective value for [`owner_can_transfer`](self.owner_can_transfer).649 pub fn owner_can_transfer(&self) -> bool {650 self.owner_can_transfer.unwrap_or(false)651 }652653 /// Get effective value for [`owner_can_transfer_instaled`](self.owner_can_transfer_instaled).654 pub fn owner_can_transfer_instaled(&self) -> bool {655 self.owner_can_transfer.is_some()656 }657658 /// Get effective value for [`owner_can_destroy`](self.owner_can_destroy).659 pub fn owner_can_destroy(&self) -> bool {660 self.owner_can_destroy.unwrap_or(true)661 }662663 /// Get effective value for [`transfers_enabled`](self.transfers_enabled).664 pub fn transfers_enabled(&self) -> bool {665 self.transfers_enabled.unwrap_or(true)666 }667668 /// Get effective value for [`sponsored_data_rate_limit`](self.sponsored_data_rate_limit).669 pub fn sponsored_data_rate_limit(&self) -> Option<u32> {670 match self671 .sponsored_data_rate_limit672 .unwrap_or(SponsoringRateLimit::SponsoringDisabled)673 {674 SponsoringRateLimit::SponsoringDisabled => None,675 SponsoringRateLimit::Blocks(v) => Some(v.min(MAX_SPONSOR_TIMEOUT)),676 }677 }678}679680/// Permissions on certain operations within a collection.681///682/// Some fields are wrapped in [`Option`], where `None` means chain default.683///684/// Update with `pallet_common::Pallet::clamp_permissions`.685#[derive(Encode, Decode, Debug, Default, Clone, PartialEq, TypeInfo, MaxEncodedLen)]686#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]687// When adding/removing fields from this struct - don't forget to also update `pallet_common::Pallet::clamp_permissions`.688// TODO: move `pallet_common::Pallet::clamp_permissions` into `impl CollectionPermissions`.689pub struct CollectionPermissions {690 /// Access mode.691 ///692 /// * Default - [`AccessMode::Normal`].693 pub access: Option<AccessMode>,694695 /// Minting allowance.696 ///697 /// * Default - **false**.698 pub mint_mode: Option<bool>,699700 /// Permissions for nesting.701 ///702 /// * Default703 /// - `token_owner` - **false**704 /// - `collection_admin` - **false**705 /// - `restricted` - **None**706 pub nesting: Option<NestingPermissions>,707}708709impl CollectionPermissions {710 /// Get effective value for [`access`](self.access).711 pub fn access(&self) -> AccessMode {712 self.access.unwrap_or(AccessMode::Normal)713 }714715 /// Get effective value for [`mint_mode`](self.mint_mode).716 pub fn mint_mode(&self) -> bool {717 self.mint_mode.unwrap_or(false)718 }719720 /// Get effective value for [`nesting`](self.nesting).721 pub fn nesting(&self) -> &NestingPermissions {722 static DEFAULT: NestingPermissions = NestingPermissions {723 token_owner: false,724 collection_admin: false,725 restricted: None,726 #[cfg(feature = "runtime-benchmarks")]727 permissive: false,728 };729 self.nesting.as_ref().unwrap_or(&DEFAULT)730 }731}732733/// Inner set for collections allowed to nest.734type OwnerRestrictedSetInner = BoundedBTreeSet<CollectionId, ConstU32<16>>;735736/// Wraper for collections set allowing nest.737#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative)]738#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]739#[derivative(Debug)]740pub struct OwnerRestrictedSet(741 #[cfg_attr(feature = "serde1", serde(with = "bounded::set_serde"))]742 #[derivative(Debug(format_with = "bounded::set_debug"))]743 pub OwnerRestrictedSetInner,744);745746impl OwnerRestrictedSet {747 /// Create new set.748 pub fn new() -> Self {749 Self(Default::default())750 }751}752impl core::ops::Deref for OwnerRestrictedSet {753 type Target = OwnerRestrictedSetInner;754 fn deref(&self) -> &Self::Target {755 &self.0756 }757}758impl core::ops::DerefMut for OwnerRestrictedSet {759 fn deref_mut(&mut self) -> &mut Self::Target {760 &mut self.0761 }762}763764/// Part of collection permissions, if set, defines who is able to nest tokens into other tokens.765#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, MaxEncodedLen, Derivative)]766#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]767#[derivative(Debug)]768pub struct NestingPermissions {769 /// Owner of token can nest tokens under it.770 pub token_owner: bool,771 /// Admin of token collection can nest tokens under token.772 pub collection_admin: bool,773 /// If set - only tokens from specified collections can be nested.774 pub restricted: Option<OwnerRestrictedSet>,775776 #[cfg(feature = "runtime-benchmarks")]777 /// Anyone can nest tokens, mutually exclusive with `token_owner`, `admin`.778 pub permissive: bool,779}780781/// Enum denominating how often can sponsoring occur if it is enabled.782///783/// Used for [`collection limits`](CollectionLimits).784#[derive(Encode, Decode, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)]785#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]786pub enum SponsoringRateLimit {787 /// Sponsoring is disabled, and the collection sponsor will not pay for transactions788 SponsoringDisabled,789 /// Once per how many blocks can sponsorship of a transaction type occur790 Blocks(u32),791}792793/// Data used to describe an NFT at creation.794#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]795#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]796#[derivative(Debug)]797pub struct CreateNftData {798 /// Key-value pairs used to describe the token as metadata799 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]800 #[derivative(Debug(format_with = "bounded::vec_debug"))]801 /// Properties that wil be assignet to created item.802 pub properties: CollectionPropertiesVec,803}804805/// Data used to describe a Fungible token at creation.806#[derive(Encode, Decode, MaxEncodedLen, Default, Debug, Clone, PartialEq, TypeInfo)]807#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]808pub struct CreateFungibleData {809 /// Number of fungible coins minted810 pub value: u128,811}812813/// Data used to describe a Refungible token at creation.814#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]815#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]816#[derivative(Debug)]817pub struct CreateReFungibleData {818 /// Number of pieces the RFT is split into819 pub pieces: u128,820821 /// Key-value pairs used to describe the token as metadata822 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]823 #[derivative(Debug(format_with = "bounded::vec_debug"))]824 pub properties: CollectionPropertiesVec,825}826827// TODO: remove this.828#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]829#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]830pub enum MetaUpdatePermission {831 ItemOwner,832 Admin,833 None,834}835836/// Enum holding data used for creation of all three item types.837/// Unified data for create item.838#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]839#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]840pub enum CreateItemData {841 /// Data for create NFT.842 NFT(CreateNftData),843 /// Data for create Fungible item.844 Fungible(CreateFungibleData),845 /// Data for create ReFungible item.846 ReFungible(CreateReFungibleData),847}848849/// Extended data for create NFT.850#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]851#[derivative(Debug)]852pub struct CreateNftExData<CrossAccountId> {853 /// Properties that wil be assignet to created item.854 #[derivative(Debug(format_with = "bounded::vec_debug"))]855 pub properties: CollectionPropertiesVec,856857 /// Owner of creating item.858 pub owner: CrossAccountId,859}860861/// Extended data for create ReFungible item.862#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]863#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]864pub struct CreateRefungibleExMultipleOwners<CrossAccountId> {865 #[derivative(Debug(format_with = "bounded::map_debug"))]866 pub users: BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,867 #[derivative(Debug(format_with = "bounded::vec_debug"))]868 pub properties: CollectionPropertiesVec,869}870871/// Extended data for create ReFungible item.872#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]873#[derivative(Debug(bound = "CrossAccountId: fmt::Debug"))]874pub struct CreateRefungibleExSingleOwner<CrossAccountId> {875 pub user: CrossAccountId,876 pub pieces: u128,877 #[derivative(Debug(format_with = "bounded::vec_debug"))]878 pub properties: CollectionPropertiesVec,879}880881/// Unified extended data for creating item.882#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]883#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]884pub enum CreateItemExData<CrossAccountId> {885 /// Extended data for create NFT.886 NFT(887 #[derivative(Debug(format_with = "bounded::vec_debug"))]888 BoundedVec<CreateNftExData<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,889 ),890891 /// Extended data for create Fungible item.892 Fungible(893 #[derivative(Debug(format_with = "bounded::map_debug"))]894 BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,895 ),896897 /// Extended data for create ReFungible item in case of898 /// many tokens, each may have only one owner899 RefungibleMultipleItems(900 #[derivative(Debug(format_with = "bounded::vec_debug"))]901 BoundedVec<CreateRefungibleExSingleOwner<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,902 ),903904 /// Extended data for create ReFungible item in case of905 /// single token, which may have many owners906 RefungibleMultipleOwners(CreateRefungibleExMultipleOwners<CrossAccountId>),907}908909impl From<CreateNftData> for CreateItemData {910 fn from(item: CreateNftData) -> Self {911 CreateItemData::NFT(item)912 }913}914915impl From<CreateReFungibleData> for CreateItemData {916 fn from(item: CreateReFungibleData) -> Self {917 CreateItemData::ReFungible(item)918 }919}920921impl From<CreateFungibleData> for CreateItemData {922 fn from(item: CreateFungibleData) -> Self {923 CreateItemData::Fungible(item)924 }925}926927/// Token's address, dictated by its collection and token IDs.928#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]929#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]930// todo possibly rename to be used generally as an address pair931pub struct TokenChild {932 /// Token id.933 pub token: TokenId,934935 /// Collection id.936 pub collection: CollectionId,937}938939/// Collection statistics.940#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug, TypeInfo)]941#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]942pub struct CollectionStats {943 /// Number of created items.944 pub created: u32,945946 /// Number of burned items.947 pub destroyed: u32,948949 /// Number of current items.950 pub alive: u32,951}952953/// This type works like [`PhantomData`] but supports generating _scale-info_ descriptions to generate node metadata.954#[derive(Encode, Decode, Clone, Debug)]955#[cfg_attr(feature = "std", derive(PartialEq))]956pub struct PhantomType<T>(core::marker::PhantomData<T>);957958impl<T: TypeInfo + 'static> TypeInfo for PhantomType<T> {959 type Identity = PhantomType<T>;960961 fn type_info() -> scale_info::Type {962 use scale_info::{963 Type, Path,964 build::{FieldsBuilder, UnnamedFields},965 form::MetaForm,966 type_params,967 };968 Type::builder()969 .path(Path::new("up_data_structs", "PhantomType"))970 .type_params(type_params!(T))971 .composite(972 <FieldsBuilder<MetaForm, UnnamedFields>>::default().field(|b| b.ty::<[T; 0]>()),973 )974 }975}976impl<T> MaxEncodedLen for PhantomType<T> {977 fn max_encoded_len() -> usize {978 0979 }980}981982/// Bounded vector of bytes.983pub type BoundedBytes<S> = BoundedVec<u8, S>;984985/// Extra properties for external collections.986pub type AuxPropertyValue = BoundedBytes<ConstU32<MAX_AUX_PROPERTY_VALUE_LENGTH>>;987988/// Property key.989pub type PropertyKey = BoundedBytes<ConstU32<MAX_PROPERTY_KEY_LENGTH>>;990991/// Property value.992pub type PropertyValue = BoundedBytes<ConstU32<MAX_PROPERTY_VALUE_LENGTH>>;993994/// Property permission.995#[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone)]996#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]997pub struct PropertyPermission {998 /// Permission to change the property and property permission.999 ///1000 /// If it **false** then you can not change corresponding property even if [`collection_admin`] and [`token_owner`] are **true**.1001 pub mutable: bool,10021003 /// Change permission for the collection administrator.1004 pub collection_admin: bool,10051006 /// Permission to change the property for the owner of the token.1007 pub token_owner: bool,1008}10091010impl PropertyPermission {1011 /// Creates mutable property permission but changes restricted for collection admin and token owner.1012 pub fn none() -> Self {1013 Self {1014 mutable: true,1015 collection_admin: false,1016 token_owner: false,1017 }1018 }1019}10201021/// Property is simpl key-value record.1022#[derive(Encode, Decode, Debug, TypeInfo, Clone, PartialEq, MaxEncodedLen)]1023#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1024pub struct Property {1025 /// Property key.1026 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1027 pub key: PropertyKey,10281029 /// Property value.1030 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1031 pub value: PropertyValue,1032}10331034impl Into<(PropertyKey, PropertyValue)> for Property {1035 fn into(self) -> (PropertyKey, PropertyValue) {1036 (self.key, self.value)1037 }1038}10391040/// Record for proprty key permission.1041#[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone)]1042#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]1043pub struct PropertyKeyPermission {1044 /// Key.1045 #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]1046 pub key: PropertyKey,10471048 /// Permission.1049 pub permission: PropertyPermission,1050}10511052impl Into<(PropertyKey, PropertyPermission)> for PropertyKeyPermission {1053 fn into(self) -> (PropertyKey, PropertyPermission) {1054 (self.key, self.permission)1055 }1056}10571058/// Errors for properties actions.1059#[derive(Debug)]1060pub enum PropertiesError {1061 /// The space allocated for properties has run out.1062 ///1063 /// * Limit for colection - [`MAX_COLLECTION_PROPERTIES_SIZE`].1064 /// * Limit for token - [`MAX_TOKEN_PROPERTIES_SIZE`].1065 NoSpaceForProperty,10661067 /// The property limit has been reached.1068 ///1069 /// * Limit - [`MAX_PROPERTIES_PER_ITEM`].1070 PropertyLimitReached,10711072 /// Property key contains not allowed character.1073 InvalidCharacterInPropertyKey,10741075 /// Property key length is too long.1076 ///1077 /// * Limit - [`MAX_PROPERTY_KEY_LENGTH`].1078 PropertyKeyIsTooLong,10791080 /// Property key is empty.1081 EmptyPropertyKey,1082}10831084/// Marker for scope of property.1085///1086/// Scoped property can't be changed by user. Used for external collections.1087#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Clone, Copy)]1088pub enum PropertyScope {1089 None,1090 Rmrk,1091}10921093impl PropertyScope {1094 /// Apply scope to property key.1095 pub fn apply(self, key: PropertyKey) -> Result<PropertyKey, PropertiesError> {1096 let scope_str: &[u8] = match self {1097 Self::None => return Ok(key),1098 Self::Rmrk => b"rmrk",1099 };11001101 [scope_str, b":", key.as_slice()]1102 .concat()1103 .try_into()1104 .map_err(|_| PropertiesError::PropertyKeyIsTooLong)1105 }1106}11071108/// Trait for operate with properties.1109pub trait TrySetProperty: Sized {1110 type Value;11111112 /// Try to set property with scope.1113 fn try_scoped_set(1114 &mut self,1115 scope: PropertyScope,1116 key: PropertyKey,1117 value: Self::Value,1118 ) -> Result<(), PropertiesError>;11191120 /// Try to set property with scope from iterator.1121 fn try_scoped_set_from_iter<I, KV>(1122 &mut self,1123 scope: PropertyScope,1124 iter: I,1125 ) -> Result<(), PropertiesError>1126 where1127 I: Iterator<Item = KV>,1128 KV: Into<(PropertyKey, Self::Value)>,1129 {1130 for kv in iter {1131 let (key, value) = kv.into();1132 self.try_scoped_set(scope, key, value)?;1133 }11341135 Ok(())1136 }11371138 /// Try to set property.1139 fn try_set(&mut self, key: PropertyKey, value: Self::Value) -> Result<(), PropertiesError> {1140 self.try_scoped_set(PropertyScope::None, key, value)1141 }11421143 /// Try to set property from iterator.1144 fn try_set_from_iter<I, KV>(&mut self, iter: I) -> Result<(), PropertiesError>1145 where1146 I: Iterator<Item = KV>,1147 KV: Into<(PropertyKey, Self::Value)>,1148 {1149 self.try_scoped_set_from_iter(PropertyScope::None, iter)1150 }1151}11521153/// Wrapped map for storing properties.1154#[derive(Encode, Decode, TypeInfo, Derivative, Clone, PartialEq, MaxEncodedLen)]1155#[derivative(Default(bound = ""))]1156pub struct PropertiesMap<Value>(1157 BoundedBTreeMap<PropertyKey, Value, ConstU32<MAX_PROPERTIES_PER_ITEM>>,1158);11591160impl<Value> PropertiesMap<Value> {1161 /// Create new property map.1162 pub fn new() -> Self {1163 Self(BoundedBTreeMap::new())1164 }11651166 /// Remove property from map.1167 pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<Value>, PropertiesError> {1168 Self::check_property_key(key)?;11691170 Ok(self.0.remove(key))1171 }11721173 /// Get property with appropriate key from map.1174 pub fn get(&self, key: &PropertyKey) -> Option<&Value> {1175 self.0.get(key)1176 }11771178 /// Check if map contains key.1179 pub fn contains_key(&self, key: &PropertyKey) -> bool {1180 self.0.contains_key(key)1181 }11821183 /// Check if map contains key with key validation.1184 fn check_property_key(key: &PropertyKey) -> Result<(), PropertiesError> {1185 if key.is_empty() {1186 return Err(PropertiesError::EmptyPropertyKey);1187 }11881189 for byte in key.as_slice().iter() {1190 let byte = *byte;11911192 if !byte.is_ascii_alphanumeric() && byte != b'_' && byte != b'-' && byte != b'.' {1193 return Err(PropertiesError::InvalidCharacterInPropertyKey);1194 }1195 }11961197 Ok(())1198 }1199}12001201impl<Value> IntoIterator for PropertiesMap<Value> {1202 type Item = (PropertyKey, Value);1203 type IntoIter = <1204 BoundedBTreeMap<1205 PropertyKey,1206 Value,1207 ConstU32<MAX_PROPERTIES_PER_ITEM>1208 > as IntoIterator1209 >::IntoIter;12101211 fn into_iter(self) -> Self::IntoIter {1212 self.0.into_iter()1213 }1214}12151216impl<Value> TrySetProperty for PropertiesMap<Value> {1217 type Value = Value;12181219 fn try_scoped_set(1220 &mut self,1221 scope: PropertyScope,1222 key: PropertyKey,1223 value: Self::Value,1224 ) -> Result<(), PropertiesError> {1225 Self::check_property_key(&key)?;12261227 let key = scope.apply(key)?;1228 self.01229 .try_insert(key, value)1230 .map_err(|_| PropertiesError::PropertyLimitReached)?;12311232 Ok(())1233 }1234}12351236/// Alias for property permissions map.1237pub type PropertiesPermissionMap = PropertiesMap<PropertyPermission>;12381239/// Wrapper for properties map with consumed space control.1240#[derive(Encode, Decode, TypeInfo, Clone, PartialEq, MaxEncodedLen)]1241pub struct Properties {1242 map: PropertiesMap<PropertyValue>,1243 consumed_space: u32,1244 space_limit: u32,1245}12461247impl Properties {1248 /// Create new properies container.1249 pub fn new(space_limit: u32) -> Self {1250 Self {1251 map: PropertiesMap::new(),1252 consumed_space: 0,1253 space_limit,1254 }1255 }12561257 /// Remove propery with appropiate key.1258 pub fn remove(&mut self, key: &PropertyKey) -> Result<Option<PropertyValue>, PropertiesError> {1259 let value = self.map.remove(key)?;12601261 if let Some(ref value) = value {1262 let value_len = value.len() as u32;1263 self.consumed_space -= value_len;1264 }12651266 Ok(value)1267 }12681269 /// Get property with appropriate key.1270 pub fn get(&self, key: &PropertyKey) -> Option<&PropertyValue> {1271 self.map.get(key)1272 }1273}12741275impl IntoIterator for Properties {1276 type Item = (PropertyKey, PropertyValue);1277 type IntoIter = <PropertiesMap<PropertyValue> as IntoIterator>::IntoIter;12781279 fn into_iter(self) -> Self::IntoIter {1280 self.map.into_iter()1281 }1282}12831284impl TrySetProperty for Properties {1285 type Value = PropertyValue;12861287 fn try_scoped_set(1288 &mut self,1289 scope: PropertyScope,1290 key: PropertyKey,1291 value: Self::Value,1292 ) -> Result<(), PropertiesError> {1293 let value_len = value.len();12941295 if self.consumed_space as usize + value_len > self.space_limit as usize1296 && !cfg!(feature = "runtime-benchmarks")1297 {1298 return Err(PropertiesError::NoSpaceForProperty);1299 }13001301 self.map.try_scoped_set(scope, key, value)?;13021303 self.consumed_space += value_len as u32;13041305 Ok(())1306 }1307}13081309/// Utility struct for using in `StorageMap`.1310pub struct CollectionProperties;13111312impl Get<Properties> for CollectionProperties {1313 fn get() -> Properties {1314 Properties::new(MAX_COLLECTION_PROPERTIES_SIZE)1315 }1316}13171318/// Utility struct for using in `StorageMap`.1319pub struct TokenProperties;13201321impl Get<Properties> for TokenProperties {1322 fn get() -> Properties {1323 Properties::new(MAX_TOKEN_PROPERTIES_SIZE)1324 }1325}13261327// RMRK1328// todo document?1329parameter_types! {1330 #[derive(PartialEq, TypeInfo)]1331 pub const RmrkStringLimit: u32 = 128;1332 #[derive(PartialEq)]1333 pub const RmrkCollectionSymbolLimit: u32 = MAX_TOKEN_PREFIX_LENGTH;1334 #[derive(PartialEq)]1335 pub const RmrkResourceSymbolLimit: u32 = 10;1336 #[derive(PartialEq)]1337 pub const RmrkBaseSymbolLimit: u32 = MAX_TOKEN_PREFIX_LENGTH;1338 #[derive(PartialEq)]1339 pub const RmrkKeyLimit: u32 = 32;1340 #[derive(PartialEq)]1341 pub const RmrkValueLimit: u32 = 256;1342 #[derive(PartialEq)]1343 pub const RmrkMaxCollectionsEquippablePerPart: u32 = 100;1344 #[derive(PartialEq)]1345 pub const MaxPropertiesPerTheme: u32 = 5;1346 #[derive(PartialEq)]1347 pub const RmrkPartsLimit: u32 = 25;1348 #[derive(PartialEq)]1349 pub const RmrkMaxPriorities: u32 = 25;1350 #[derive(PartialEq)]1351 pub const MaxResourcesOnMint: u32 = 100;1352}13531354impl From<RmrkCollectionId> for CollectionId {1355 fn from(id: RmrkCollectionId) -> Self {1356 Self(id)1357 }1358}13591360impl From<RmrkNftId> for TokenId {1361 fn from(id: RmrkNftId) -> Self {1362 Self(id)1363 }1364}13651366pub type RmrkCollectionInfo<AccountId> =1367 CollectionInfo<RmrkString, RmrkCollectionSymbol, AccountId>;1368pub type RmrkInstanceInfo<AccountId> = NftInfo<AccountId, Permill, RmrkString>;1369pub type RmrkResourceInfo = ResourceInfo<RmrkString, RmrkBoundedParts>;1370pub type RmrkPropertyInfo = PropertyInfo<RmrkKeyString, RmrkValueString>;1371pub type RmrkBaseInfo<AccountId> = BaseInfo<AccountId, RmrkString>;1372pub type BoundedEquippableCollectionIds =1373 BoundedVec<RmrkCollectionId, RmrkMaxCollectionsEquippablePerPart>;1374pub type RmrkPartType = PartType<RmrkString, BoundedEquippableCollectionIds>;1375pub type RmrkEquippableList = EquippableList<BoundedEquippableCollectionIds>;1376pub type RmrkThemeProperty = ThemeProperty<RmrkString>;1377pub type RmrkTheme = Theme<RmrkString, Vec<RmrkThemeProperty>>;1378pub type RmrkBoundedTheme = Theme<RmrkString, BoundedVec<RmrkThemeProperty, MaxPropertiesPerTheme>>;1379pub type RmrkResourceTypes = ResourceTypes<RmrkString, RmrkBoundedParts>;13801381pub type RmrkBasicResource = BasicResource<RmrkString>;1382pub type RmrkComposableResource = ComposableResource<RmrkString, RmrkBoundedParts>;1383pub type RmrkSlotResource = SlotResource<RmrkString>;13841385pub type RmrkString = BoundedVec<u8, RmrkStringLimit>;1386pub type RmrkCollectionSymbol = BoundedVec<u8, RmrkCollectionSymbolLimit>;1387pub type RmrkBaseSymbol = BoundedVec<u8, RmrkBaseSymbolLimit>;1388pub type RmrkKeyString = BoundedVec<u8, RmrkKeyLimit>;1389pub type RmrkValueString = BoundedVec<u8, RmrkValueLimit>;1390pub type RmrkBoundedResource = BoundedVec<u8, RmrkResourceSymbolLimit>;1391pub type RmrkBoundedParts = BoundedVec<RmrkPartId, RmrkPartsLimit>; // todo make sure it is needed13921393pub type RmrkRpcString = Vec<u8>;1394pub type RmrkThemeName = RmrkRpcString;1395pub type RmrkPropertyKey = RmrkRpcString;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`.